diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index 5348370e..c8107706 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -686,6 +686,12 @@ typedef int (*ssh_auth_callback) (const char *prompt, char *buf, size_t len, /** @} */ +enum ssh_file_format_e { + SSH_FILE_FORMAT_DEFAULT = 0, + SSH_FILE_FORMAT_OPENSSH, + SSH_FILE_FORMAT_PEM, +}; + LIBSSH_API ssh_key ssh_key_new(void); #define SSH_KEY_FREE(x) \ do { if ((x) != NULL) { ssh_key_free(x); x = NULL; } } while(0) @@ -712,6 +718,13 @@ LIBSSH_API int ssh_pki_export_privkey_base64(const ssh_key privkey, ssh_auth_callback auth_fn, void *auth_data, char **b64_key); +LIBSSH_API int +ssh_pki_export_privkey_base64_format(const ssh_key privkey, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + char **b64_key, + enum ssh_file_format_e format); LIBSSH_API int ssh_pki_import_privkey_file(const char *filename, const char *passphrase, ssh_auth_callback auth_fn, @@ -722,6 +735,13 @@ LIBSSH_API int ssh_pki_export_privkey_file(const ssh_key privkey, ssh_auth_callback auth_fn, void *auth_data, const char *filename); +LIBSSH_API int +ssh_pki_export_privkey_file_format(const ssh_key privkey, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + const char *filename, + enum ssh_file_format_e format); LIBSSH_API int ssh_pki_copy_cert_to_privkey(const ssh_key cert_key, ssh_key privkey); diff --git a/include/libssh/pki.h b/include/libssh/pki.h index 575d442d..efb9bdbf 100644 --- a/include/libssh/pki.h +++ b/include/libssh/pki.h @@ -153,6 +153,10 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob, int ssh_pki_import_cert_blob(const ssh_string cert_blob, ssh_key *pkey); +/* SSH Private Key Functions */ +int ssh_pki_export_privkey_blob(const ssh_key key, + ssh_string *pblob); + /* SSH Signing Functions */ ssh_string ssh_pki_do_sign(ssh_session session, ssh_buffer sigbuf, diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h index c63e129d..2061ebd7 100644 --- a/include/libssh/pki_priv.h +++ b/include/libssh/pki_priv.h @@ -92,7 +92,7 @@ int pki_pubkey_build_rsa(ssh_key key, ssh_string e, ssh_string n); int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e); -ssh_string pki_publickey_to_blob(const ssh_key key); +ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type); /* SSH Private Key Functions */ int pki_privkey_build_rsa(ssh_key key, @@ -106,7 +106,6 @@ int pki_privkey_build_ecdsa(ssh_key key, int nid, ssh_string e, ssh_string exp); -ssh_string pki_publickey_to_blob(const ssh_key key); /* SSH Signature Functions */ ssh_signature pki_sign_data(const ssh_key privkey, @@ -143,6 +142,7 @@ int pki_ed25519_key_cmp(const ssh_key k1, enum ssh_keycmp_e what); int pki_ed25519_key_dup(ssh_key new_key, const ssh_key key); int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key); +int pki_ed25519_private_key_to_blob(ssh_buffer buffer, const ssh_key privkey); ssh_string pki_ed25519_signature_to_blob(ssh_signature sig); int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob); int pki_privkey_build_ed25519(ssh_key key, diff --git a/src/libssh.map b/src/libssh.map index 5797261a..e0d310dd 100644 --- a/src/libssh.map +++ b/src/libssh.map @@ -469,5 +469,7 @@ LIBSSH_AFTER_4_9_0 sftp_aio_free; sftp_aio_wait_read; sftp_aio_wait_write; + ssh_pki_export_privkey_base64_format; + ssh_pki_export_privkey_file_format; } LIBSSH_4_9_0; diff --git a/src/pki.c b/src/pki.c index f43f835a..b5d423a2 100644 --- a/src/pki.c +++ b/src/pki.c @@ -836,6 +836,88 @@ int ssh_pki_import_privkey_base64(const char *b64_key, return SSH_OK; } + + + /** + * @brief Convert a private key to a base64 encoded key in given format + * + * @param[in] privkey The private key to export. + * + * @param[in] passphrase The passphrase to use to encrypt the key with or + * NULL. An empty string means no passphrase. + * + * @param[in] auth_fn An auth function you may want to use or NULL. + * + * @param[in] auth_data Private data passed to the auth function. + * + * @param[out] b64_key A pointer to store the allocated base64 encoded key. You + * need to free the buffer using ssh_string_from_char(). + * + * @param[in] format The file format (OpenSSH, PEM, or default) + * + * @return SSH_OK on success, SSH_ERROR on error. + * + * @see ssh_string_free_char() + */ +int +ssh_pki_export_privkey_base64_format(const ssh_key privkey, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + char **b64_key, + enum ssh_file_format_e format) +{ + ssh_string blob = NULL; + char *b64 = NULL; + + if (privkey == NULL || !ssh_key_is_private(privkey)) { + return SSH_ERROR; + } + + /* The PEM export is supported only with OpenSSL. We fall back to + * OpenSSH key format elsewhere */ + if (format == SSH_FILE_FORMAT_DEFAULT) { +#ifdef HAVE_LIBCRYPTO + if (privkey->type != SSH_KEYTYPE_ED25519) { + format = SSH_FILE_FORMAT_PEM; + } else { +#else + if (1) { +#endif /* HAVE_LIBCRYPTO */ + format = SSH_FILE_FORMAT_OPENSSH; + } + } + + switch (format) { + case SSH_FILE_FORMAT_DEFAULT: + case SSH_FILE_FORMAT_PEM: + blob = pki_private_key_to_pem(privkey, + passphrase, + auth_fn, + auth_data); + break; + case SSH_FILE_FORMAT_OPENSSH: + blob = ssh_pki_openssh_privkey_export(privkey, + passphrase, + auth_fn, + auth_data); + break; + } + if (blob == NULL) { + return SSH_ERROR; + } + + b64 = strndup(ssh_string_data(blob), ssh_string_len(blob)); + SSH_STRING_FREE(blob); + if (b64 == NULL) { + return SSH_ERROR; + } + + *b64_key = b64; + + return SSH_OK; +} + /** * @brief Convert a private key to a pem base64 encoded key, or OpenSSH format for * keytype ssh-ed25519 @@ -862,39 +944,16 @@ int ssh_pki_export_privkey_base64(const ssh_key privkey, void *auth_data, char **b64_key) { - ssh_string blob = NULL; - char *b64 = NULL; - - if (privkey == NULL || !ssh_key_is_private(privkey)) { - return SSH_ERROR; - } - - if (privkey->type == SSH_KEYTYPE_ED25519){ - blob = ssh_pki_openssh_privkey_export(privkey, - passphrase, - auth_fn, - auth_data); - } else { - blob = pki_private_key_to_pem(privkey, - passphrase, - auth_fn, - auth_data); - } - if (blob == NULL) { - return SSH_ERROR; - } - - b64 = strndup(ssh_string_data(blob), ssh_string_len(blob)); - SSH_STRING_FREE(blob); - if (b64 == NULL) { - return SSH_ERROR; - } - - *b64_key = b64; - - return SSH_OK; + return ssh_pki_export_privkey_base64_format(privkey, + passphrase, + auth_fn, + auth_data, + b64_key, + SSH_FILE_FORMAT_DEFAULT); } + + /** * @brief Import a private key from a file or a PKCS #11 device. * @@ -1002,8 +1061,7 @@ int ssh_pki_import_privkey_file(const char *filename, } /** - * @brief Export a private key to a pem file on disk, or OpenSSH format for - * keytype ssh-ed25519 + * @brief Export a private key to a file in format specified in the argument * * @param[in] privkey The private key to export. * @@ -1016,16 +1074,21 @@ int ssh_pki_import_privkey_file(const char *filename, * * @param[in] filename The path where to store the pem file. * + * @param[in] format The file format (OpenSSH, PEM, or default) + * * @return SSH_OK on success, SSH_ERROR on error. */ -int ssh_pki_export_privkey_file(const ssh_key privkey, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data, - const char *filename) + +int +ssh_pki_export_privkey_file_format(const ssh_key privkey, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + const char *filename, + enum ssh_file_format_e format) { - ssh_string blob; - FILE *fp; + ssh_string blob = NULL; + FILE *fp = NULL; int rc; if (privkey == NULL || !ssh_key_is_private(privkey)) { @@ -1040,16 +1103,34 @@ int ssh_pki_export_privkey_file(const ssh_key privkey, return SSH_EOF; } - if (privkey->type == SSH_KEYTYPE_ED25519){ - blob = ssh_pki_openssh_privkey_export(privkey, - passphrase, - auth_fn, - auth_data); - } else { + /* The PEM export is supported only with OpenSSL. We fall back to + * OpenSSH key format elsewhere */ + if (format == SSH_FILE_FORMAT_DEFAULT) { +#ifdef HAVE_LIBCRYPTO + if (privkey->type != SSH_KEYTYPE_ED25519) { + format = SSH_FILE_FORMAT_PEM; + } else { +#else + if (1) { +#endif /* HAVE_LIBCRYPTO */ + format = SSH_FILE_FORMAT_OPENSSH; + } + } + + switch (format) { + case SSH_FILE_FORMAT_DEFAULT: + case SSH_FILE_FORMAT_PEM: blob = pki_private_key_to_pem(privkey, passphrase, auth_fn, auth_data); + break; + case SSH_FILE_FORMAT_OPENSSH: + blob = ssh_pki_openssh_privkey_export(privkey, + passphrase, + auth_fn, + auth_data); + break; } if (blob == NULL) { fclose(fp); @@ -1068,6 +1149,38 @@ int ssh_pki_export_privkey_file(const ssh_key privkey, return SSH_OK; } +/** + * @brief Export a private key to a pem file on disk, or OpenSSH format for + * keytype ssh-ed25519 + * + * @param[in] privkey The private key to export. + * + * @param[in] passphrase The passphrase to use to encrypt the key with or + * NULL. An empty string means no passphrase. + * + * @param[in] auth_fn An auth function you may want to use or NULL. + * + * @param[in] auth_data Private data passed to the auth function. + * + * @param[in] filename The path where to store the pem file. + * + * @return SSH_OK on success, SSH_ERROR on error. + */ +int +ssh_pki_export_privkey_file(const ssh_key privkey, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + const char *filename) +{ + return ssh_pki_export_privkey_file_format(privkey, + passphrase, + auth_fn, + auth_data, + filename, + SSH_FILE_FORMAT_DEFAULT); +} + /* temporary function to migrate seamlessly to ssh_key */ ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) { @@ -2035,7 +2148,42 @@ int ssh_pki_export_pubkey_blob(const ssh_key key, return SSH_OK; } - blob = pki_publickey_to_blob(key); + blob = pki_key_to_blob(key, SSH_KEY_PUBLIC); + if (blob == NULL) { + return SSH_ERROR; + } + + *pblob = blob; + return SSH_OK; +} + +/** + * @internal + * + * @brief Create a key_blob from a private key. + * + * The "key_blob" is encoded as per draft-miller-ssh-agent-08 section 4.2 + * "Adding keys to the agent" for any of the supported key types. + * + * @param[in] key A private key to create the private ssh_string from. + * + * @param[out] pblob A pointer to store the newly allocated key blob. You + * need to free it using ssh_string_free(). + * + * @return SSH_OK on success, SSH_ERROR otherwise. + * + * @see ssh_string_free() + */ +int ssh_pki_export_privkey_blob(const ssh_key key, + ssh_string *pblob) +{ + ssh_string blob; + + if (key == NULL) { + return SSH_OK; + } + + blob = pki_key_to_blob(key, SSH_KEY_PRIVATE); if (blob == NULL) { return SSH_ERROR; } @@ -2066,7 +2214,7 @@ int ssh_pki_export_pubkey_base64(const ssh_key key, return SSH_ERROR; } - key_blob = pki_publickey_to_blob(key); + key_blob = pki_key_to_blob(key, SSH_KEY_PUBLIC); if (key_blob == NULL) { return SSH_ERROR; } diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c index 92101c4e..82afdf8e 100644 --- a/src/pki_container_openssh.c +++ b/src/pki_container_openssh.c @@ -394,37 +394,6 @@ ssh_key ssh_pki_openssh_pubkey_import(const char *text_key) } -/** @internal - * @brief exports a private key to a string blob. - * @param[in] privkey private key to convert - * @param[out] buffer buffer to write the blob in. - * @returns SSH_OK on success - * @warning only supports ed25519 key type at the moment. - */ -static int pki_openssh_export_privkey_blob(const ssh_key privkey, - ssh_buffer buffer) -{ - int rc; - - if (privkey->type != SSH_KEYTYPE_ED25519) { - SSH_LOG(SSH_LOG_TRACE, "Type %s not supported", privkey->type_c); - return SSH_ERROR; - } - if (privkey->ed25519_privkey == NULL || - privkey->ed25519_pubkey == NULL) { - return SSH_ERROR; - } - rc = ssh_buffer_pack(buffer, - "sdPdPP", - privkey->type_c, - (uint32_t)ED25519_KEY_LEN, - (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey, - (uint32_t)(2 * ED25519_KEY_LEN), - (size_t)ED25519_KEY_LEN, privkey->ed25519_privkey, - (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey); - return rc; -} - /** @internal * @brief encrypts an ed25519 private key blob * @@ -536,8 +505,8 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, ssh_auth_callback auth_fn, void *auth_data) { - ssh_buffer buffer; - ssh_string str = NULL; + ssh_buffer buffer = NULL; + ssh_string str = NULL, blob = NULL; ssh_string pubkey_s=NULL; ssh_buffer privkey_buffer = NULL; uint32_t rnd; @@ -554,17 +523,13 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, if (privkey == NULL) { return NULL; } - if (privkey->type != SSH_KEYTYPE_ED25519){ - SSH_LOG(SSH_LOG_TRACE, "Unsupported key type %s", privkey->type_c); - return NULL; - } if (passphrase != NULL || auth_fn != NULL){ SSH_LOG(SSH_LOG_DEBUG, "Enabling encryption for private key export"); to_encrypt = 1; } buffer = ssh_buffer_new(); - pubkey_s = pki_publickey_to_blob(privkey); - if(buffer == NULL || pubkey_s == NULL){ + rc = ssh_pki_export_pubkey_blob(privkey, &pubkey_s); + if (buffer == NULL || rc != SSH_OK) { goto error; } @@ -578,22 +543,17 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, goto error; } - /* checkint1 & 2 */ + rc = ssh_pki_export_privkey_blob(privkey, &blob); + if (rc != SSH_OK) { + goto error; + } + rc = ssh_buffer_pack(privkey_buffer, - "dd", + "ddPs", + rnd, /* checkint 1 & 2 */ rnd, - rnd); - if (rc == SSH_ERROR){ - goto error; - } - - rc = pki_openssh_export_privkey_blob(privkey, privkey_buffer); - if (rc == SSH_ERROR){ - goto error; - } - - /* comment */ - rc = ssh_buffer_pack(privkey_buffer, "s", "" /* comment */); + ssh_string_len(blob), ssh_string_data(blob), + "" /* comment */); if (rc == SSH_ERROR){ goto error; } @@ -710,6 +670,8 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, } error: + ssh_string_burn(blob); + ssh_string_free(blob); if (privkey_buffer != NULL) { void *bufptr = ssh_buffer_get(privkey_buffer); explicit_bzero(bufptr, ssh_buffer_get_len(privkey_buffer)); diff --git a/src/pki_crypto.c b/src/pki_crypto.c index 4b60a709..f4ce8bdf 100644 --- a/src/pki_crypto.c +++ b/src/pki_crypto.c @@ -1309,18 +1309,9 @@ fail: rc = evp_build_pkey("RSA", param_bld, &(key->key), EVP_PKEY_KEYPAIR); if (rc != SSH_OK) { - rc = SSH_ERROR; - goto fail; - } - - rc = EVP_PKEY_set_bn_param(key->key, OSSL_PKEY_PARAM_RSA_FACTOR1, bp); - if (rc != 1) { - rc = SSH_ERROR; - goto fail; - } - - rc = EVP_PKEY_set_bn_param(key->key, OSSL_PKEY_PARAM_RSA_FACTOR2, bq); - if (rc != 1) { + SSH_LOG(SSH_LOG_WARNING, + "Failed to import private key: %s\n", + ERR_error_string(ERR_get_error(), NULL)); rc = SSH_ERROR; goto fail; } @@ -1412,7 +1403,7 @@ fail: #endif /* OPENSSL_VERSION_NUMBER */ } -ssh_string pki_publickey_to_blob(const ssh_key key) +ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type) { ssh_buffer buffer; ssh_string type_s; @@ -1422,10 +1413,13 @@ ssh_string pki_publickey_to_blob(const ssh_key key) ssh_string p = NULL; ssh_string g = NULL; ssh_string q = NULL; + ssh_string d = NULL; + ssh_string iqmp = NULL; int rc; #if OPENSSL_VERSION_NUMBER >= 0x30000000L BIGNUM *bp = NULL, *bq = NULL, *bg = NULL, *bpub_key = NULL, - *bn = NULL, *be = NULL; + *bn = NULL, *be = NULL, + *bd = NULL, *biqmp = NULL; OSSL_PARAM *params = NULL; #endif /* OPENSSL_VERSION_NUMBER */ @@ -1460,7 +1454,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) case SSH_KEYTYPE_RSA: case SSH_KEYTYPE_RSA1: { #if OPENSSL_VERSION_NUMBER < 0x30000000L - const BIGNUM *be, *bn; + const BIGNUM *be = NULL, *bn = NULL; const RSA *key_rsa = EVP_PKEY_get0_RSA(key->key); RSA_get0_key(key_rsa, &bn, &be, NULL); #else @@ -1498,13 +1492,133 @@ ssh_string pki_publickey_to_blob(const ssh_key key) goto fail; } - if (ssh_buffer_add_ssh_string(buffer, e) < 0) { - goto fail; - } - if (ssh_buffer_add_ssh_string(buffer, n) < 0) { - goto fail; - } + if (type == SSH_KEY_PUBLIC) { + /* The N and E parts are swapped in the public key export ! */ + rc = ssh_buffer_add_ssh_string(buffer, e); + if (rc < 0) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, n); + if (rc < 0) { + goto fail; + } + } else if (type == SSH_KEY_PRIVATE) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const BIGNUM *bd, *biqmp, *bp, *bq; + RSA_get0_key(key_rsa, NULL, NULL, &bd); + RSA_get0_factors(key_rsa, &bp, &bq); + RSA_get0_crt_params(key_rsa, NULL, NULL, &biqmp); +#else + rc = EVP_PKEY_todata(key->key, EVP_PKEY_KEYPAIR, ¶ms); + if (rc != 1) { + goto fail; + } + out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_D); + if (out_param == NULL) { + SSH_LOG(SSH_LOG_TRACE, "RSA: No param D has been found"); + goto fail; + } + rc = OSSL_PARAM_get_BN(out_param, &bd); + if (rc != 1) { + goto fail; + } + + out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR1); + if (out_param == NULL) { + SSH_LOG(SSH_LOG_TRACE, "RSA: No param P has been found"); + goto fail; + } + rc = OSSL_PARAM_get_BN(out_param, &bp); + if (rc != 1) { + goto fail; + } + + out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR2); + if (out_param == NULL) { + SSH_LOG(SSH_LOG_TRACE, "RSA: No param Q has been found"); + goto fail; + } + rc = OSSL_PARAM_get_BN(out_param, &bq); + if (rc != 1) { + goto fail; + } + + out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_COEFFICIENT1); + if (out_param == NULL) { + SSH_LOG(SSH_LOG_TRACE, "RSA: No param IQMP has been found"); + goto fail; + } + rc = OSSL_PARAM_get_BN(out_param, &biqmp); + if (rc != 1) { + goto fail; + } +#endif /* OPENSSL_VERSION_NUMBER */ + rc = ssh_buffer_add_ssh_string(buffer, n); + if (rc < 0) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, e); + if (rc < 0) { + goto fail; + } + + d = ssh_make_bignum_string((BIGNUM *)bd); + if (d == NULL) { + goto fail; + } + + iqmp = ssh_make_bignum_string((BIGNUM *)biqmp); + if (iqmp == NULL) { + goto fail; + } + + p = ssh_make_bignum_string((BIGNUM *)bp); + if (p == NULL) { + goto fail; + } + + q = ssh_make_bignum_string((BIGNUM *)bq); + if (q == NULL) { + goto fail; + } + + rc = ssh_buffer_add_ssh_string(buffer, d); + if (rc < 0) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, iqmp); + if (rc < 0) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, p); + if (rc < 0) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, q); + if (rc < 0) { + goto fail; + } + + ssh_string_burn(d); + SSH_STRING_FREE(d); + d = NULL; + ssh_string_burn(iqmp); + SSH_STRING_FREE(iqmp); + iqmp = NULL; + ssh_string_burn(p); + SSH_STRING_FREE(p); + p = NULL; + ssh_string_burn(q); + SSH_STRING_FREE(q); + q = NULL; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + bignum_safe_free(bd); + bignum_safe_free(biqmp); + bignum_safe_free(bp); + bignum_safe_free(bq); +#endif /* OPENSSL_VERSION_NUMBER */ + } ssh_string_burn(e); SSH_STRING_FREE(e); e = NULL; @@ -1520,13 +1634,23 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } case SSH_KEYTYPE_ED25519: case SSH_KEYTYPE_SK_ED25519: - rc = pki_ed25519_public_key_to_blob(buffer, key); - if (rc == SSH_ERROR){ - goto fail; - } - if (key->type == SSH_KEYTYPE_SK_ED25519 && - ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) { - goto fail; + if (type == SSH_KEY_PUBLIC) { + rc = pki_ed25519_public_key_to_blob(buffer, key); + if (rc == SSH_ERROR){ + goto fail; + } + /* public key can contain certificate sk information */ + if (key->type == SSH_KEYTYPE_SK_ED25519) { + rc = ssh_buffer_add_ssh_string(buffer, key->sk_application); + if (rc < 0) { + goto fail; + } + } + } else { + rc = pki_ed25519_private_key_to_blob(buffer, key); + if (rc == SSH_ERROR){ + goto fail; + } } break; case SSH_KEYTYPE_ECDSA_P256: @@ -1544,6 +1668,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) #else const EC_GROUP *group = NULL; const EC_POINT *point = NULL; + const BIGNUM *exp = NULL; EC_KEY *ec = NULL; #endif /* OPENSSL_VERSION_NUMBER */ @@ -1628,15 +1753,52 @@ ssh_string pki_publickey_to_blob(const ssh_key key) ssh_string_burn(e); SSH_STRING_FREE(e); e = NULL; + if (type == SSH_KEY_PRIVATE) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + rc = EVP_PKEY_todata(key->key, EVP_PKEY_KEYPAIR, ¶ms); + if (rc < 0) { + goto fail; + } + + locate_param = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY); + rc = OSSL_PARAM_get_BN(locate_param, &bd); + if (rc != 1) { + goto fail; + } + d = ssh_make_bignum_string((BIGNUM *)bd); + if (d == NULL) { + goto fail; + } + if (ssh_buffer_add_ssh_string(buffer, d) < 0) { + goto fail; + } +#else + exp = EC_KEY_get0_private_key(ec); + if (exp == NULL) { + goto fail; + } + d = ssh_make_bignum_string((BIGNUM *)exp); + if (d == NULL) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, d); + if (rc < 0) { + goto fail; + } +#endif /* OPENSSL_VERSION_NUMBER */ + ssh_string_burn(d); + SSH_STRING_FREE(d); + d = NULL; + } else if (key->type == SSH_KEYTYPE_SK_ECDSA) { + /* public key can contain certificate sk information */ + rc = ssh_buffer_add_ssh_string(buffer, key->sk_application); + if (rc < 0) { + goto fail; + } + } #if OPENSSL_VERSION_NUMBER >= 0x30000000L OSSL_PARAM_free(params); #endif /* OPENSSL_VERSION_NUMBER */ - - if (key->type == SSH_KEYTYPE_SK_ECDSA && - ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) { - goto fail; - } - break; } #endif /* HAVE_OPENSSL_ECC */ @@ -1672,6 +1834,10 @@ fail: SSH_STRING_FREE(q); ssh_string_burn(n); SSH_STRING_FREE(n); + ssh_string_burn(d); + SSH_STRING_FREE(d); + ssh_string_burn(iqmp); + SSH_STRING_FREE(iqmp); #if OPENSSL_VERSION_NUMBER >= 0x30000000L bignum_safe_free(bp); bignum_safe_free(bq); @@ -1679,6 +1845,8 @@ fail: bignum_safe_free(bpub_key); bignum_safe_free(bn); bignum_safe_free(be); + bignum_safe_free(bd); + bignum_safe_free(biqmp); OSSL_PARAM_free(params); #endif /* OPENSSL_VERSION_NUMBER */ diff --git a/src/pki_ed25519_common.c b/src/pki_ed25519_common.c index 3b165e2c..03859f7c 100644 --- a/src/pki_ed25519_common.c +++ b/src/pki_ed25519_common.c @@ -206,6 +206,34 @@ int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key) return rc; } +/** @internal + * @brief exports a ed25519 private key to a string blob. + * @param[in] privkey private key to convert + * @param[out] buffer buffer to write the blob in. + * @returns SSH_OK on success + */ +int pki_ed25519_private_key_to_blob(ssh_buffer buffer, const ssh_key privkey) +{ + int rc; + + if (privkey->type != SSH_KEYTYPE_ED25519) { + SSH_LOG(SSH_LOG_TRACE, "Type %s not supported", privkey->type_c); + return SSH_ERROR; + } + if (privkey->ed25519_privkey == NULL || + privkey->ed25519_pubkey == NULL) { + return SSH_ERROR; + } + rc = ssh_buffer_pack(buffer, + "dPdPP", + (uint32_t)ED25519_KEY_LEN, + (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey, + (uint32_t)(2 * ED25519_KEY_LEN), + (size_t)ED25519_KEY_LEN, privkey->ed25519_privkey, + (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey); + return rc; +} + /** * @internal * diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c index 0a864493..65bb77e6 100644 --- a/src/pki_gcrypt.c +++ b/src/pki_gcrypt.c @@ -1366,16 +1366,18 @@ int pki_key_compare(const ssh_key k1, return 0; } -ssh_string pki_publickey_to_blob(const ssh_key key) +ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type) { ssh_buffer buffer; ssh_string type_s; ssh_string str = NULL; ssh_string e = NULL; ssh_string n = NULL; + ssh_string d = NULL; ssh_string p = NULL; ssh_string g = NULL; ssh_string q = NULL; + ssh_string u = NULL; int rc; buffer = ssh_buffer_new(); @@ -1423,30 +1425,108 @@ ssh_string pki_publickey_to_blob(const ssh_key key) goto fail; } - rc = ssh_buffer_add_ssh_string(buffer, e); - if (rc < 0) { - goto fail; - } - rc = ssh_buffer_add_ssh_string(buffer, n); - if (rc < 0) { - goto fail; - } + if (type == SSH_KEY_PUBLIC) { + /* The N and E parts are swapped in the public key export ! */ + rc = ssh_buffer_add_ssh_string(buffer, e); + if (rc < 0) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, n); + if (rc < 0) { + goto fail; + } + } else if (type == SSH_KEY_PRIVATE) { + rc = ssh_buffer_add_ssh_string(buffer, n); + if (rc < 0) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, e); + if (rc < 0) { + goto fail; + } + d = ssh_sexp_extract_mpi(key->rsa, + "d", + GCRYMPI_FMT_USG, + GCRYMPI_FMT_STD); + if (d == NULL) { + goto fail; + } + + p = ssh_sexp_extract_mpi(key->rsa, + "p", + GCRYMPI_FMT_USG, + GCRYMPI_FMT_STD); + if (p == NULL) { + goto fail; + } + + q = ssh_sexp_extract_mpi(key->rsa, + "q", + GCRYMPI_FMT_USG, + GCRYMPI_FMT_STD); + if (q == NULL) { + goto fail; + } + + u = ssh_sexp_extract_mpi(key->rsa, + "u", + GCRYMPI_FMT_USG, + GCRYMPI_FMT_STD); + if (u == NULL) { + goto fail; + } + + rc = ssh_buffer_add_ssh_string(buffer, d); + if (rc < 0) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, u); + if (rc < 0) { + goto fail; + } + /* Swap the P and Q as the iqmp in gcrypt is ipmq ... */ + rc = ssh_buffer_add_ssh_string(buffer, q); + if (rc < 0) { + goto fail; + } + rc = ssh_buffer_add_ssh_string(buffer, p); + if (rc < 0) { + goto fail; + } + ssh_string_burn(d); + SSH_STRING_FREE(d); + ssh_string_burn(p); + SSH_STRING_FREE(p); + ssh_string_burn(q); + SSH_STRING_FREE(q); + ssh_string_burn(u); + SSH_STRING_FREE(u); + } ssh_string_burn(e); SSH_STRING_FREE(e); ssh_string_burn(n); SSH_STRING_FREE(n); - break; case SSH_KEYTYPE_ED25519: case SSH_KEYTYPE_SK_ED25519: - rc = pki_ed25519_public_key_to_blob(buffer, key); - if (rc != SSH_OK){ - goto fail; - } - if (key->type == SSH_KEYTYPE_SK_ED25519 && - ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) { - goto fail; + if (type == SSH_KEY_PUBLIC) { + rc = pki_ed25519_public_key_to_blob(buffer, key); + if (rc == SSH_ERROR) { + goto fail; + } + /* public key can contain certificate sk information */ + if (key->type == SSH_KEYTYPE_SK_ED25519) { + rc = ssh_buffer_add_ssh_string(buffer, key->sk_application); + if (rc < 0) { + goto fail; + } + } + } else { + rc = pki_ed25519_private_key_to_blob(buffer, key); + if (rc == SSH_ERROR) { + goto fail; + } } break; case SSH_KEYTYPE_ECDSA_P256: @@ -1457,22 +1537,19 @@ ssh_string pki_publickey_to_blob(const ssh_key key) type_s = ssh_string_from_char( pki_key_ecdsa_nid_to_char(key->ecdsa_nid)); if (type_s == NULL) { - SSH_BUFFER_FREE(buffer); - return NULL; + goto fail; } rc = ssh_buffer_add_ssh_string(buffer, type_s); SSH_STRING_FREE(type_s); if (rc < 0) { - SSH_BUFFER_FREE(buffer); - return NULL; + goto fail; } e = ssh_sexp_extract_mpi(key->ecdsa, "q", GCRYMPI_FMT_STD, GCRYMPI_FMT_STD); if (e == NULL) { - SSH_BUFFER_FREE(buffer); - return NULL; + goto fail; } rc = ssh_buffer_add_ssh_string(buffer, e); @@ -1484,9 +1561,27 @@ ssh_string pki_publickey_to_blob(const ssh_key key) SSH_STRING_FREE(e); e = NULL; - if (key->type == SSH_KEYTYPE_SK_ECDSA && - ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) { - goto fail; + if (type == SSH_KEY_PRIVATE) { + d = ssh_sexp_extract_mpi(key->ecdsa, "d", GCRYMPI_FMT_STD, + GCRYMPI_FMT_STD); + if (d == NULL) { + goto fail; + } + + rc = ssh_buffer_add_ssh_string(buffer, d); + if (rc < 0) { + goto fail; + } + + ssh_string_burn(d); + SSH_STRING_FREE(d); + d = NULL; + } else if (key->type == SSH_KEYTYPE_SK_ECDSA) { + /* public key can contain certificate sk information */ + rc = ssh_buffer_add_ssh_string(buffer, key->sk_application); + if (rc < 0) { + goto fail; + } } break; diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c index e047239e..d3fda0ae 100644 --- a/src/pki_mbedcrypto.c +++ b/src/pki_mbedcrypto.c @@ -878,7 +878,7 @@ static const char* pki_key_ecdsa_nid_to_char(int nid) return "unknown"; } -ssh_string pki_publickey_to_blob(const ssh_key key) +ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type) { ssh_buffer buffer = NULL; ssh_string type_s = NULL; @@ -888,6 +888,10 @@ ssh_string pki_publickey_to_blob(const ssh_key key) #if MBEDTLS_VERSION_MAJOR > 2 mbedtls_mpi E; mbedtls_mpi N; + mbedtls_mpi D; + mbedtls_mpi IQMP; + mbedtls_mpi P; + mbedtls_mpi Q; #endif int rc; @@ -961,21 +965,124 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } #endif - if (ssh_buffer_add_ssh_string(buffer, e) < 0) { - goto fail; - } + if (type == SSH_KEY_PUBLIC) { + /* The N and E parts are swapped in the public key export ! */ + rc = ssh_buffer_add_ssh_string(buffer, e); + if (rc < 0) { + goto fail; + } - if (ssh_buffer_add_ssh_string(buffer, n) < 0) { - goto fail; - } + rc = ssh_buffer_add_ssh_string(buffer, n); + if (rc < 0) { + goto fail; + } + } else if (type == SSH_KEY_PRIVATE) { + ssh_string p = NULL; + ssh_string q = NULL; + ssh_string d = NULL; + ssh_string iqmp = NULL; + rc = ssh_buffer_add_ssh_string(buffer, n); + if (rc < 0) { + goto fail; + } + + rc = ssh_buffer_add_ssh_string(buffer, e); + if (rc < 0) { + goto fail; + } + +#if MBEDTLS_VERSION_MAJOR > 2 + rc = mbedtls_rsa_export(rsa, NULL, &P, &Q, &D, NULL); + if (rc != 0) { + goto fail; + } + + p = ssh_make_bignum_string(&P); + if (p == NULL) { + goto fail; + } + + q = ssh_make_bignum_string(&Q); + if (q == NULL) { + goto fail; + } + + d = ssh_make_bignum_string(&D); + if (d == NULL) { + goto fail; + } + rc = mbedtls_rsa_export_crt(rsa, NULL, NULL, &IQMP) + if (rc != 0) { + goto fail; + } + + iqmp = ssh_make_bignum_string(&IQMP); + if (iqmp == NULL) { + goto fail; + } + +#else + p = ssh_make_bignum_string(&rsa->P); + if (p == NULL) { + goto fail; + } + + q = ssh_make_bignum_string(&rsa->Q); + if (q == NULL) { + goto fail; + } + + d = ssh_make_bignum_string(&rsa->D); + if (d == NULL) { + goto fail; + } + + iqmp = ssh_make_bignum_string(&rsa->QP); + if (iqmp == NULL) { + goto fail; + } +#endif + + rc = ssh_buffer_add_ssh_string(buffer, d); + if (rc < 0) { + goto fail; + } + + rc = ssh_buffer_add_ssh_string(buffer, iqmp); + if (rc < 0) { + goto fail; + } + + rc = ssh_buffer_add_ssh_string(buffer, p); + if (rc < 0) { + goto fail; + } + + rc = ssh_buffer_add_ssh_string(buffer, q); + if (rc < 0) { + goto fail; + } + + ssh_string_burn(d); + SSH_STRING_FREE(d); + d = NULL; + ssh_string_burn(iqmp); + SSH_STRING_FREE(iqmp); + iqmp = NULL; + ssh_string_burn(p); + SSH_STRING_FREE(p); + p = NULL; + ssh_string_burn(q); + SSH_STRING_FREE(q); + q = NULL; + } ssh_string_burn(e); SSH_STRING_FREE(e); e = NULL; ssh_string_burn(n); SSH_STRING_FREE(n); n = NULL; - break; } case SSH_KEYTYPE_ECDSA_P256: @@ -1013,21 +1120,51 @@ ssh_string pki_publickey_to_blob(const ssh_key key) SSH_STRING_FREE(e); e = NULL; - if (key->type == SSH_KEYTYPE_SK_ECDSA && - ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) { - goto fail; - } + if (type == SSH_KEY_PRIVATE) { + ssh_string d = NULL; + d = ssh_make_bignum_string(&key->ecdsa->MBEDTLS_PRIVATE(d)); + if (d == NULL) { + SSH_BUFFER_FREE(buffer); + return NULL; + } + + rc = ssh_buffer_add_ssh_string(buffer, d); + if (rc < 0) { + goto fail; + } + + ssh_string_burn(d); + SSH_STRING_FREE(d); + d = NULL; + } else if (key->type == SSH_KEYTYPE_SK_ECDSA) { + /* public key can contain certificate sk information */ + rc = ssh_buffer_add_ssh_string(buffer, key->sk_application); + if (rc < 0) { + goto fail; + } + + } break; case SSH_KEYTYPE_ED25519: case SSH_KEYTYPE_SK_ED25519: - rc = pki_ed25519_public_key_to_blob(buffer, key); - if (rc != SSH_OK) { - goto fail; - } - if (key->type == SSH_KEYTYPE_SK_ED25519 && - ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) { - goto fail; + if (type == SSH_KEY_PUBLIC) { + rc = pki_ed25519_public_key_to_blob(buffer, key); + if (rc == SSH_ERROR) { + goto fail; + } + /* public key can contain certificate sk information */ + if (key->type == SSH_KEYTYPE_SK_ED25519) { + rc = ssh_buffer_add_ssh_string(buffer, key->sk_application); + if (rc < 0) { + goto fail; + } + } + } else { + rc = pki_ed25519_private_key_to_blob(buffer, key); + if (rc == SSH_ERROR) { + goto fail; + } } break; default: