mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-11 18:50:28 +09:00
pki: add security key file import/export functionality
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
@@ -164,6 +164,11 @@ int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type);
|
|||||||
#endif /* WITH_PKCS11_URI */
|
#endif /* WITH_PKCS11_URI */
|
||||||
|
|
||||||
bool ssh_key_size_allowed_rsa(int min_size, ssh_key key);
|
bool ssh_key_size_allowed_rsa(int min_size, ssh_key key);
|
||||||
|
|
||||||
|
/* Security Key Helper Functions */
|
||||||
|
int pki_buffer_pack_sk_priv_data(ssh_buffer buffer, const ssh_key key);
|
||||||
|
int pki_buffer_unpack_sk_priv_data(ssh_buffer buffer, ssh_key key);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
133
src/pki.c
133
src/pki.c
@@ -997,10 +997,15 @@ ssh_pki_export_privkey_base64_format(const ssh_key privkey,
|
|||||||
/*
|
/*
|
||||||
* For historic reasons, the Ed25519 keys are exported in OpenSSH file
|
* For historic reasons, the Ed25519 keys are exported in OpenSSH file
|
||||||
* format by default also when built with OpenSSL.
|
* format by default also when built with OpenSSL.
|
||||||
|
*
|
||||||
|
* The FIDO2/U2F security keys are an extension to the SSH protocol
|
||||||
|
* proposed by OpenSSH, and do not have any representation in PEM format.
|
||||||
|
* So, they are always exported in the OpenSSH file format.
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_LIBCRYPTO
|
#ifdef HAVE_LIBCRYPTO
|
||||||
if (format == SSH_FILE_FORMAT_DEFAULT &&
|
if (format == SSH_FILE_FORMAT_DEFAULT &&
|
||||||
privkey->type != SSH_KEYTYPE_ED25519) {
|
privkey->type != SSH_KEYTYPE_ED25519 &&
|
||||||
|
!is_sk_key_type(privkey->type)) {
|
||||||
format = SSH_FILE_FORMAT_PEM;
|
format = SSH_FILE_FORMAT_PEM;
|
||||||
}
|
}
|
||||||
#endif /* HAVE_LIBCRYPTO */
|
#endif /* HAVE_LIBCRYPTO */
|
||||||
@@ -1224,10 +1229,16 @@ ssh_pki_export_privkey_file_format(const ssh_key privkey,
|
|||||||
/*
|
/*
|
||||||
* For historic reasons, the Ed25519 keys are exported in OpenSSH file
|
* For historic reasons, the Ed25519 keys are exported in OpenSSH file
|
||||||
* format by default also when built with OpenSSL.
|
* format by default also when built with OpenSSL.
|
||||||
|
*
|
||||||
|
* The FIDO2/U2F security keys are an extension to the SSH protocol
|
||||||
|
* proposed by OpenSSH, and do not have any representation in PEM format.
|
||||||
|
* So, they are always exported in the OpenSSH file format.
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_LIBCRYPTO
|
#ifdef HAVE_LIBCRYPTO
|
||||||
if (format == SSH_FILE_FORMAT_DEFAULT &&
|
if (format == SSH_FILE_FORMAT_DEFAULT &&
|
||||||
privkey->type != SSH_KEYTYPE_ED25519) {
|
privkey->type != SSH_KEYTYPE_ED25519 &&
|
||||||
|
!is_sk_key_type(privkey->type)) {
|
||||||
|
|
||||||
format = SSH_FILE_FORMAT_PEM;
|
format = SSH_FILE_FORMAT_PEM;
|
||||||
}
|
}
|
||||||
#endif /* HAVE_LIBCRYPTO */
|
#endif /* HAVE_LIBCRYPTO */
|
||||||
@@ -1453,6 +1464,38 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SSH_KEYTYPE_SK_ECDSA: {
|
||||||
|
ssh_string type_str = NULL;
|
||||||
|
ssh_string pubkey = NULL;
|
||||||
|
int nid;
|
||||||
|
|
||||||
|
rc = ssh_buffer_unpack(buffer, "SS", &type_str, &pubkey);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pki_buffer_unpack_sk_priv_data(buffer, key);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
SSH_STRING_FREE(type_str);
|
||||||
|
SSH_STRING_FREE(pubkey);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
nid = pki_key_ecdsa_nid_from_name(ssh_string_get_char(type_str));
|
||||||
|
SSH_STRING_FREE(type_str);
|
||||||
|
|
||||||
|
if (nid == -1) {
|
||||||
|
SSH_STRING_FREE(pubkey);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pki_pubkey_build_ecdsa(key, nid, pubkey);
|
||||||
|
SSH_STRING_FREE(pubkey);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
#endif /* HAVE_ECC */
|
#endif /* HAVE_ECC */
|
||||||
case SSH_KEYTYPE_ED25519: {
|
case SSH_KEYTYPE_ED25519: {
|
||||||
ssh_string pubkey = NULL, privkey = NULL;
|
ssh_string pubkey = NULL, privkey = NULL;
|
||||||
@@ -1478,14 +1521,38 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SSH_KEYTYPE_SK_ED25519: {
|
||||||
|
ssh_string pubkey = NULL;
|
||||||
|
|
||||||
|
if (ssh_fips_mode()) {
|
||||||
|
SSH_LOG(SSH_LOG_TRACE, "Ed25519 keys not supported in FIPS mode");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ssh_buffer_unpack(buffer, "S", &pubkey);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pki_buffer_unpack_sk_priv_data(buffer, key);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
SSH_STRING_FREE(pubkey);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pki_pubkey_build_ed25519(key, pubkey);
|
||||||
|
SSH_STRING_FREE(pubkey);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case SSH_KEYTYPE_RSA_CERT01:
|
case SSH_KEYTYPE_RSA_CERT01:
|
||||||
case SSH_KEYTYPE_ECDSA_P256_CERT01:
|
case SSH_KEYTYPE_ECDSA_P256_CERT01:
|
||||||
case SSH_KEYTYPE_ECDSA_P384_CERT01:
|
case SSH_KEYTYPE_ECDSA_P384_CERT01:
|
||||||
case SSH_KEYTYPE_ECDSA_P521_CERT01:
|
case SSH_KEYTYPE_ECDSA_P521_CERT01:
|
||||||
case SSH_KEYTYPE_ED25519_CERT01:
|
case SSH_KEYTYPE_ED25519_CERT01:
|
||||||
case SSH_KEYTYPE_SK_ECDSA:
|
|
||||||
case SSH_KEYTYPE_SK_ECDSA_CERT01:
|
case SSH_KEYTYPE_SK_ECDSA_CERT01:
|
||||||
case SSH_KEYTYPE_SK_ED25519:
|
|
||||||
case SSH_KEYTYPE_SK_ED25519_CERT01:
|
case SSH_KEYTYPE_SK_ED25519_CERT01:
|
||||||
case SSH_KEYTYPE_RSA1:
|
case SSH_KEYTYPE_RSA1:
|
||||||
case SSH_KEYTYPE_UNKNOWN:
|
case SSH_KEYTYPE_UNKNOWN:
|
||||||
@@ -2232,6 +2299,64 @@ int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey,
|
|||||||
return SSH_OK;
|
return SSH_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @brief Pack security key private data into a buffer.
|
||||||
|
*
|
||||||
|
* This function packs the common security key fields (application, flags,
|
||||||
|
* key handle, and reserved data) into a buffer.
|
||||||
|
* This is used for both ECDSA and Ed25519 security keys when exporting
|
||||||
|
* private key data.
|
||||||
|
*
|
||||||
|
* @param[in] buffer The buffer to pack the security key data into.
|
||||||
|
*
|
||||||
|
* @param[in] key The security key containing the data to pack.
|
||||||
|
* Must be a security key type (SK_ECDSA or SK_ED25519).
|
||||||
|
*
|
||||||
|
* @return SSH_OK on success, SSH_ERROR on error.
|
||||||
|
*
|
||||||
|
* @see ssh_buffer_pack()
|
||||||
|
*/
|
||||||
|
int pki_buffer_pack_sk_priv_data(ssh_buffer buffer, ssh_key key)
|
||||||
|
{
|
||||||
|
return ssh_buffer_pack(buffer,
|
||||||
|
"SbSS",
|
||||||
|
key->sk_application,
|
||||||
|
key->sk_flags,
|
||||||
|
key->sk_key_handle,
|
||||||
|
key->sk_reserved);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @brief Unpack security key private data from a buffer.
|
||||||
|
*
|
||||||
|
* This function unpacks the common security key fields (application, flags,
|
||||||
|
* key handle, and reserved data) from a buffer.
|
||||||
|
* This is used for both ECDSA and Ed25519 security keys when importing
|
||||||
|
* private key data.
|
||||||
|
*
|
||||||
|
* @param[in] buffer The buffer to unpack the security key data from.
|
||||||
|
*
|
||||||
|
* @param[in] key The security key to store the unpacked data into.
|
||||||
|
* Must be a security key type (SK_ECDSA or SK_ED25519).
|
||||||
|
*
|
||||||
|
* @return SSH_OK on success, SSH_ERROR on error.
|
||||||
|
*
|
||||||
|
* @see ssh_buffer_unpack()
|
||||||
|
*/
|
||||||
|
int pki_buffer_unpack_sk_priv_data(ssh_buffer buffer, ssh_key key)
|
||||||
|
{
|
||||||
|
return ssh_buffer_unpack(buffer,
|
||||||
|
"SbSS",
|
||||||
|
&key->sk_application,
|
||||||
|
&key->sk_flags,
|
||||||
|
&key->sk_key_handle,
|
||||||
|
&key->sk_reserved);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1785,7 +1785,7 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == SSH_KEY_PRIVATE) {
|
if (type == SSH_KEY_PRIVATE && key->type == SSH_KEYTYPE_ED25519) {
|
||||||
key_len = 0;
|
key_len = 0;
|
||||||
rc = EVP_PKEY_get_raw_private_key(key->key, NULL, &key_len);
|
rc = EVP_PKEY_get_raw_private_key(key->key, NULL, &key_len);
|
||||||
if (rc != 1) {
|
if (rc != 1) {
|
||||||
@@ -1831,15 +1831,22 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
}
|
}
|
||||||
explicit_bzero(ed25519_privkey, ED25519_KEY_LEN);
|
explicit_bzero(ed25519_privkey, ED25519_KEY_LEN);
|
||||||
SAFE_FREE(ed25519_privkey);
|
SAFE_FREE(ed25519_privkey);
|
||||||
} else {
|
} else if (type == SSH_KEY_PRIVATE &&
|
||||||
|
key->type == SSH_KEYTYPE_SK_ED25519) {
|
||||||
|
|
||||||
|
rc = pki_buffer_pack_sk_priv_data(buffer, key);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else if (type == SSH_KEY_PUBLIC &&
|
||||||
|
key->type == SSH_KEYTYPE_SK_ED25519) {
|
||||||
/* public key can contain certificate sk information */
|
/* public key can contain certificate sk information */
|
||||||
if (key->type == SSH_KEYTYPE_SK_ED25519) {
|
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
|
||||||
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
|
if (rc != SSH_OK) {
|
||||||
if (rc < 0) {
|
goto fail;
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SAFE_FREE(ed25519_pubkey);
|
SAFE_FREE(ed25519_pubkey);
|
||||||
break;
|
break;
|
||||||
case SSH_KEYTYPE_ECDSA_P256:
|
case SSH_KEYTYPE_ECDSA_P256:
|
||||||
@@ -1944,7 +1951,8 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
ssh_string_burn(e);
|
ssh_string_burn(e);
|
||||||
SSH_STRING_FREE(e);
|
SSH_STRING_FREE(e);
|
||||||
e = NULL;
|
e = NULL;
|
||||||
if (type == SSH_KEY_PRIVATE) {
|
|
||||||
|
if (type == SSH_KEY_PRIVATE && key->type != SSH_KEYTYPE_SK_ECDSA) {
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||||
OSSL_PARAM_free(params);
|
OSSL_PARAM_free(params);
|
||||||
rc = EVP_PKEY_todata(key->key, EVP_PKEY_KEYPAIR, ¶ms);
|
rc = EVP_PKEY_todata(key->key, EVP_PKEY_KEYPAIR, ¶ms);
|
||||||
@@ -1981,10 +1989,18 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
ssh_string_burn(d);
|
ssh_string_burn(d);
|
||||||
SSH_STRING_FREE(d);
|
SSH_STRING_FREE(d);
|
||||||
d = NULL;
|
d = NULL;
|
||||||
} else if (key->type == SSH_KEYTYPE_SK_ECDSA) {
|
} else if (type == SSH_KEY_PRIVATE &&
|
||||||
|
key->type == SSH_KEYTYPE_SK_ECDSA) {
|
||||||
|
|
||||||
|
rc = pki_buffer_pack_sk_priv_data(buffer, key);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else if (type == SSH_KEY_PUBLIC &&
|
||||||
|
key->type == SSH_KEYTYPE_SK_ECDSA) {
|
||||||
/* public key can contain certificate sk information */
|
/* public key can contain certificate sk information */
|
||||||
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
|
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
|
||||||
if (rc < 0) {
|
if (rc != SSH_OK) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1600,9 +1600,21 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rc = pki_ed25519_private_key_to_blob(buffer, key);
|
if (key->type == SSH_KEYTYPE_SK_ED25519) {
|
||||||
if (rc == SSH_ERROR) {
|
rc = pki_ed25519_public_key_to_blob(buffer, key);
|
||||||
goto fail;
|
if (rc == SSH_ERROR) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pki_buffer_pack_sk_priv_data(buffer, key);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rc = pki_ed25519_private_key_to_blob(buffer, key);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1640,7 +1652,7 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
SSH_STRING_FREE(e);
|
SSH_STRING_FREE(e);
|
||||||
e = NULL;
|
e = NULL;
|
||||||
|
|
||||||
if (type == SSH_KEY_PRIVATE) {
|
if (type == SSH_KEY_PRIVATE && !is_sk_key_type(key->type)) {
|
||||||
d = ssh_sexp_extract_mpi(key->ecdsa,
|
d = ssh_sexp_extract_mpi(key->ecdsa,
|
||||||
"d",
|
"d",
|
||||||
GCRYMPI_FMT_STD,
|
GCRYMPI_FMT_STD,
|
||||||
@@ -1657,7 +1669,14 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
ssh_string_burn(d);
|
ssh_string_burn(d);
|
||||||
SSH_STRING_FREE(d);
|
SSH_STRING_FREE(d);
|
||||||
d = NULL;
|
d = NULL;
|
||||||
} else if (key->type == SSH_KEYTYPE_SK_ECDSA) {
|
} else if (type == SSH_KEY_PRIVATE && is_sk_key_type(key->type)) {
|
||||||
|
/* Add security key private data for SK_ECDSA */
|
||||||
|
rc = pki_buffer_pack_sk_priv_data(buffer, key);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else if (type == SSH_KEY_PUBLIC &&
|
||||||
|
key->type == SSH_KEYTYPE_SK_ECDSA) {
|
||||||
/* public key can contain certificate sk information */
|
/* public key can contain certificate sk information */
|
||||||
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
|
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
|||||||
@@ -1066,7 +1066,7 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == SSH_KEY_PRIVATE) {
|
if (type == SSH_KEY_PRIVATE && key->type != SSH_KEYTYPE_SK_ECDSA) {
|
||||||
d = ssh_make_bignum_string(&key->ecdsa->MBEDTLS_PRIVATE(d));
|
d = ssh_make_bignum_string(&key->ecdsa->MBEDTLS_PRIVATE(d));
|
||||||
|
|
||||||
if (d == NULL) {
|
if (d == NULL) {
|
||||||
@@ -1078,7 +1078,14 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} else if (key->type == SSH_KEYTYPE_SK_ECDSA) {
|
} else if (type == SSH_KEY_PRIVATE &&
|
||||||
|
key->type == SSH_KEYTYPE_SK_ECDSA) {
|
||||||
|
rc = pki_buffer_pack_sk_priv_data(buffer, key);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else if (type == SSH_KEY_PUBLIC &&
|
||||||
|
key->type == SSH_KEYTYPE_SK_ECDSA) {
|
||||||
/* public key can contain certificate sk information */
|
/* public key can contain certificate sk information */
|
||||||
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
|
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
@@ -1101,9 +1108,21 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rc = pki_ed25519_private_key_to_blob(buffer, key);
|
if (key->type == SSH_KEYTYPE_SK_ED25519) {
|
||||||
if (rc == SSH_ERROR) {
|
rc = pki_ed25519_public_key_to_blob(buffer, key);
|
||||||
goto out;
|
if (rc == SSH_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pki_buffer_pack_sk_priv_data(buffer, key);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rc = pki_ed25519_private_key_to_blob(buffer, key);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user