mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-05-28 17:41:28 +03:00
pki_crypto: Support Ed25519 keys in PEM files
This adds support for Ed25519 keys from files in PEM format when using OpenSSL with Ed25519 support. The default encoding for the PEM file is expected to be PKCS#8. Encrypted files are supported. For the lack of an API, it is not possible to export keys in PEM format, only in OpenSSH format. Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
parent
61e6b6cc59
commit
a3a0529b41
@ -730,29 +730,58 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkey = EVP_PKEY_new();
|
||||
if (pkey == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (key->type) {
|
||||
case SSH_KEYTYPE_DSS:
|
||||
pkey = EVP_PKEY_new();
|
||||
if (pkey == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_set1_DSA(pkey, key->dsa);
|
||||
break;
|
||||
case SSH_KEYTYPE_RSA:
|
||||
case SSH_KEYTYPE_RSA1:
|
||||
pkey = EVP_PKEY_new();
|
||||
if (pkey == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_set1_RSA(pkey, key->rsa);
|
||||
break;
|
||||
#ifdef HAVE_ECC
|
||||
case SSH_KEYTYPE_ECDSA_P256:
|
||||
case SSH_KEYTYPE_ECDSA_P384:
|
||||
case SSH_KEYTYPE_ECDSA_P521:
|
||||
pkey = EVP_PKEY_new();
|
||||
if (pkey == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa);
|
||||
break;
|
||||
#endif
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
#ifdef HAVE_OPENSSL_ED25519
|
||||
/* In OpenSSL, the input is the private key seed only, which means
|
||||
* the first half of the SSH private key (the second half is the
|
||||
* public key) */
|
||||
pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL,
|
||||
(const uint8_t *)key->ed25519_privkey,
|
||||
ED25519_KEY_LEN);
|
||||
if (pkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create ed25519 EVP_PKEY: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Mark the operation as successful as for the other key types */
|
||||
rc = 1;
|
||||
break;
|
||||
#else
|
||||
SSH_LOG(SSH_LOG_WARN, "PEM output not supported for key type ssh-ed25519");
|
||||
goto err;
|
||||
#endif
|
||||
case SSH_KEYTYPE_DSS_CERT01:
|
||||
case SSH_KEYTYPE_RSA_CERT01:
|
||||
case SSH_KEYTYPE_ECDSA_P256_CERT01:
|
||||
@ -865,7 +894,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
|
||||
SSH_LOG(SSH_LOG_WARN,
|
||||
"Parsing private key: %s",
|
||||
ERR_error_string(ERR_get_error(),NULL));
|
||||
return NULL;
|
||||
goto fail;
|
||||
}
|
||||
type = SSH_KEYTYPE_DSS;
|
||||
break;
|
||||
@ -875,7 +904,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
|
||||
SSH_LOG(SSH_LOG_WARN,
|
||||
"Parsing private key: %s",
|
||||
ERR_error_string(ERR_get_error(),NULL));
|
||||
return NULL;
|
||||
goto fail;
|
||||
}
|
||||
type = SSH_KEYTYPE_RSA;
|
||||
break;
|
||||
@ -886,7 +915,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
|
||||
SSH_LOG(SSH_LOG_WARN,
|
||||
"Parsing private key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* pki_privatekey_type_from_string always returns P256 for ECDSA
|
||||
@ -898,6 +927,43 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_OPENSSL_ED25519
|
||||
case EVP_PKEY_ED25519:
|
||||
{
|
||||
size_t key_len;
|
||||
int evp_rc = 0;
|
||||
|
||||
/* Get the key length */
|
||||
evp_rc = EVP_PKEY_get_raw_private_key(pkey, NULL, &key_len);
|
||||
if (evp_rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw private key length: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (key_len != ED25519_KEY_LEN) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ed25519 = malloc(key_len);
|
||||
if (ed25519 == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARN, "Out of memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
evp_rc = EVP_PKEY_get_raw_private_key(pkey, (uint8_t *)ed25519,
|
||||
&key_len);
|
||||
if (evp_rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw private key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto fail;
|
||||
}
|
||||
type = SSH_KEYTYPE_ED25519;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
EVP_PKEY_free(pkey);
|
||||
@ -927,13 +993,16 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
|
||||
|
||||
return key;
|
||||
fail:
|
||||
EVP_PKEY_free(pkey);
|
||||
ssh_key_free(key);
|
||||
DSA_free(dsa);
|
||||
RSA_free(rsa);
|
||||
#ifdef HAVE_OPENSSL_ECC
|
||||
EC_KEY_free(ecdsa);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_ED25519
|
||||
SAFE_FREE(ed25519);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -602,7 +602,12 @@ static const char torture_ecdsa521_testkey_cert[] =
|
||||
* ED25519 KEYS
|
||||
****************************************************************************/
|
||||
|
||||
static const char torture_ed25519_private_testkey[] =
|
||||
static const char torture_ed25519_private_pkcs8_testkey[] =
|
||||
"-----BEGIN PRIVATE KEY-----\n"
|
||||
"MC4CAQAwBQYDK2VwBCIEIGBhcqLe61tkqVjIHKEzwB3oINasSHWGbIWXQWcLPmGN\n"
|
||||
"-----END PRIVATE KEY-----\n";
|
||||
|
||||
static const char torture_ed25519_private_openssh_testkey[] =
|
||||
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
||||
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n"
|
||||
"QyNTUxOQAAACAVlp8bgmIjsrzGC7ZIKBMhCpS1fpJTPgVOjYdz5gIqlwAAAJBzsDN1c7Az\n"
|
||||
@ -611,16 +616,24 @@ static const char torture_ed25519_private_testkey[] =
|
||||
"lLV+klM+BU6Nh3PmAiqXAAAADGFyaXNAa2FsaXg4NgE=\n"
|
||||
"-----END OPENSSH PRIVATE KEY-----\n";
|
||||
|
||||
static const char torture_ed25519_private_testkey_passphrase[] =
|
||||
static const char torture_ed25519_private_openssh_testkey_passphrase[] =
|
||||
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
||||
"b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABB3FWpQcE\n"
|
||||
"KHKq6PcjkxjmKzAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIOGFVuOyZBL0T+NR\n"
|
||||
"C7qEV9qr6QiGhz2XSXrxuQoU84FgAAAAkBlOVfS5U7FxtBEtxfxQhZjrZAj2z9d4OfGRPl\n"
|
||||
"ZfCnAJNEM3BZ3XCabsujhMkqEs9eptRfj41X6NA8aSFs5JYT+JFVfg470FKtpyUmAibMIo\n"
|
||||
"JzI41zAncFd1x7bAgO5HBDe3xNsV159D+sXRkWB9Tzk0l4F8SZvInheIS7VSbqH7t1+yDB\n"
|
||||
"Y3GsmYTDstmicanQ==\n"
|
||||
"b3BlbnNzaC1rZXktdjEAAAAACmFlczEyOC1jYmMAAAAGYmNyeXB0AAAAGAAAABDYuz+a8i\n"
|
||||
"nb/BgGjQjQtvkUAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBWWnxuCYiOyvMYL\n"
|
||||
"tkgoEyEKlLV+klM+BU6Nh3PmAiqXAAAAkOBxqvzvPSns3TbhjkCayvANI66100OELnpDOm\n"
|
||||
"JBGgXr5q846NkAovH3pmJ4O7qzPLTQ/cm0+959VUODRhM1i96qBg5MTNtV33lf5Y57Klzu\n"
|
||||
"JegbiexcqkHIzriH42K0XSOEpfW8f/rTH7ffjbE/7l8HRNwf7AmcnxLx/d8J8FTBr+8aU7\n"
|
||||
"qMU3xAJ4ixnwhYFg==\n"
|
||||
"-----END OPENSSH PRIVATE KEY-----\n";
|
||||
|
||||
static const char torture_ed25519_private_pkcs8_testkey_passphrase[] =
|
||||
"-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
|
||||
"MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAie1RBk/ub+EwICCAAw\n"
|
||||
"DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEECRLkPChQx/sZPYLdNJhxMUEQFLj\n"
|
||||
"7nelAdOx3WXIBbCOfOqg3aAn8C5cXPtIQ+fiui1V8wlXXV8RBiuDCC97ScLs91D5\n"
|
||||
"qQhQtw0vgfnq1um/izg=\n"
|
||||
"-----END ENCRYPTED PRIVATE KEY-----\n";
|
||||
|
||||
static const char torture_ed25519_public_testkey[] =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBWWnxuCYiOyvMYLtkgoEyEKlLV+klM+"
|
||||
"BU6Nh3PmAiqX aris@kalix86";
|
||||
@ -733,16 +746,19 @@ static const char *torture_get_testkey_internal(enum ssh_keytypes_e type,
|
||||
return torture_ed25519_public_testkey;
|
||||
} else if (with_passphrase) {
|
||||
if (format == 1) {
|
||||
return torture_ed25519_private_testkey_passphrase;
|
||||
return torture_ed25519_private_openssh_testkey_passphrase;
|
||||
}
|
||||
if (format == 2) {
|
||||
return torture_ed25519_private_pkcs8_testkey_passphrase;
|
||||
}
|
||||
/* ed25519 keys are not available in legacy PEM format */
|
||||
return NULL;
|
||||
}
|
||||
if (format == 1) {
|
||||
return torture_ed25519_private_testkey;
|
||||
return torture_ed25519_private_openssh_testkey;
|
||||
}
|
||||
/* ed25519 keys are not available in legacy PEM format */
|
||||
return NULL;
|
||||
return torture_ed25519_private_pkcs8_testkey;
|
||||
case SSH_KEYTYPE_DSS_CERT01:
|
||||
return torture_dsa_testkey_cert;
|
||||
case SSH_KEYTYPE_RSA_CERT01:
|
||||
|
@ -643,14 +643,131 @@ static void torture_pki_ed25519_sign(void **state)
|
||||
assert_non_null(blob);
|
||||
|
||||
assert_int_equal(ssh_string_len(blob), sizeof(ref_signature));
|
||||
assert_memory_equal(ssh_string_data(blob), ref_signature, sizeof(ref_signature));
|
||||
/* ssh_print_hexa("signature", ssh_string_data(blob), ssh_string_len(blob)); */
|
||||
assert_memory_equal(ssh_string_data(blob), ref_signature,
|
||||
sizeof(ref_signature));
|
||||
|
||||
ssh_signature_free(sig);
|
||||
SSH_KEY_FREE(privkey);
|
||||
SSH_STRING_FREE(blob);
|
||||
|
||||
}
|
||||
|
||||
static void torture_pki_ed25519_sign_openssh_privkey_passphrase(void **state)
|
||||
{
|
||||
ssh_key privkey = NULL;
|
||||
ssh_signature sig = NULL;
|
||||
ssh_string blob = NULL;
|
||||
const char *keystring = NULL;
|
||||
int rc;
|
||||
|
||||
/* Skip test if in FIPS mode */
|
||||
if (ssh_fips_mode()) {
|
||||
skip();
|
||||
}
|
||||
|
||||
(void)state;
|
||||
|
||||
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 1);
|
||||
rc = ssh_pki_import_privkey_base64(keystring,
|
||||
torture_get_testkey_passphrase(),
|
||||
NULL,
|
||||
NULL,
|
||||
&privkey);
|
||||
assert_true(rc == SSH_OK);
|
||||
assert_non_null(privkey);
|
||||
|
||||
sig = pki_do_sign(privkey, HASH, sizeof(HASH), SSH_DIGEST_AUTO);
|
||||
assert_non_null(sig);
|
||||
|
||||
blob = pki_signature_to_blob(sig);
|
||||
assert_non_null(blob);
|
||||
assert_int_equal(ssh_string_len(blob), sizeof(ref_signature));
|
||||
assert_memory_equal(ssh_string_data(blob), ref_signature,
|
||||
sizeof(ref_signature));
|
||||
|
||||
ssh_signature_free(sig);
|
||||
SSH_KEY_FREE(privkey);
|
||||
SSH_STRING_FREE(blob);
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENSSL_ED25519
|
||||
static void torture_pki_ed25519_sign_pkcs8_privkey(void **state)
|
||||
{
|
||||
ssh_key privkey = NULL;
|
||||
ssh_signature sig = NULL;
|
||||
ssh_string blob = NULL;
|
||||
const char *keystring = NULL;
|
||||
int rc;
|
||||
|
||||
/* Skip test if in FIPS mode */
|
||||
if (ssh_fips_mode()) {
|
||||
skip();
|
||||
}
|
||||
|
||||
(void)state;
|
||||
|
||||
keystring = torture_get_testkey(SSH_KEYTYPE_ED25519, 0);
|
||||
rc = ssh_pki_import_privkey_base64(keystring,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&privkey);
|
||||
assert_true(rc == SSH_OK);
|
||||
assert_non_null(privkey);
|
||||
|
||||
sig = pki_do_sign(privkey, HASH, sizeof(HASH), SSH_DIGEST_AUTO);
|
||||
assert_non_null(sig);
|
||||
|
||||
blob = pki_signature_to_blob(sig);
|
||||
assert_non_null(blob);
|
||||
assert_int_equal(ssh_string_len(blob), sizeof(ref_signature));
|
||||
assert_memory_equal(ssh_string_data(blob), ref_signature,
|
||||
sizeof(ref_signature));
|
||||
|
||||
ssh_signature_free(sig);
|
||||
SSH_KEY_FREE(privkey);
|
||||
SSH_STRING_FREE(blob);
|
||||
}
|
||||
|
||||
static void torture_pki_ed25519_sign_pkcs8_privkey_passphrase(void **state)
|
||||
{
|
||||
ssh_key privkey = NULL;
|
||||
ssh_signature sig = NULL;
|
||||
ssh_string blob = NULL;
|
||||
const char *keystring = NULL;
|
||||
int rc;
|
||||
|
||||
/* Skip test if in FIPS mode */
|
||||
if (ssh_fips_mode()) {
|
||||
skip();
|
||||
}
|
||||
|
||||
(void)state;
|
||||
|
||||
keystring = torture_get_testkey(SSH_KEYTYPE_ED25519, 1);
|
||||
rc = ssh_pki_import_privkey_base64(keystring,
|
||||
torture_get_testkey_passphrase(),
|
||||
NULL,
|
||||
NULL,
|
||||
&privkey);
|
||||
assert_true(rc == SSH_OK);
|
||||
assert_non_null(privkey);
|
||||
|
||||
sig = pki_do_sign(privkey, HASH, sizeof(HASH), SSH_DIGEST_AUTO);
|
||||
assert_non_null(sig);
|
||||
|
||||
blob = pki_signature_to_blob(sig);
|
||||
assert_non_null(blob);
|
||||
assert_int_equal(ssh_string_len(blob), sizeof(ref_signature));
|
||||
assert_memory_equal(ssh_string_data(blob), ref_signature,
|
||||
sizeof(ref_signature));
|
||||
|
||||
ssh_signature_free(sig);
|
||||
SSH_KEY_FREE(privkey);
|
||||
SSH_STRING_FREE(blob);
|
||||
}
|
||||
#endif /* HAVE_OPENSSL_ED25519 */
|
||||
|
||||
static void torture_pki_ed25519_verify(void **state){
|
||||
ssh_key pubkey = NULL;
|
||||
ssh_signature sig = NULL;
|
||||
@ -895,6 +1012,11 @@ int torture_run_tests(void) {
|
||||
teardown),
|
||||
cmocka_unit_test(torture_pki_ed25519_import_privkey_base64_passphrase),
|
||||
cmocka_unit_test(torture_pki_ed25519_sign),
|
||||
cmocka_unit_test(torture_pki_ed25519_sign_openssh_privkey_passphrase),
|
||||
#ifdef HAVE_OPENSSL_ED25519
|
||||
cmocka_unit_test(torture_pki_ed25519_sign_pkcs8_privkey),
|
||||
cmocka_unit_test(torture_pki_ed25519_sign_pkcs8_privkey_passphrase),
|
||||
#endif
|
||||
cmocka_unit_test(torture_pki_ed25519_verify),
|
||||
cmocka_unit_test(torture_pki_ed25519_verify_bad),
|
||||
cmocka_unit_test(torture_pki_ed25519_privkey_dup),
|
||||
|
Loading…
x
Reference in New Issue
Block a user