summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnderson Toshiyuki Sasaki <ansasaki@redhat.com>2019-08-30 18:41:16 +0200
committerAnderson Toshiyuki Sasaki <ansasaki@redhat.com>2019-09-30 16:58:07 +0200
commit4f7cb6076a1d43696528927fc1161e0b81d88e47 (patch)
tree34c8c0015b0a1e3cfb1c2ed87597f5491b04b34c
parent3f6820694eebbcb8575e4748163ebf5208272dff (diff)
downloadlibssh-stable-0.9.tar.gz
libssh-stable-0.9.tar.xz
libssh-stable-0.9.zip
pki_crypto: Support Ed25519 keys in PEM filesstable-0.9
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> (cherry picked from commit a3a0529b41e5ce4789cc8a5bd5e09b4ed15efe32)
-rw-r--r--src/pki_crypto.c87
-rw-r--r--tests/torture_key.c38
-rw-r--r--tests/unittests/torture_pki_ed25519.c126
3 files changed, 229 insertions, 22 deletions
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 13198249..4517e11d 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -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
@@ -899,6 +928,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);
SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d",
@@ -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;
}
diff --git a/tests/torture_key.c b/tests/torture_key.c
index e0c7643f..58540268 100644
--- a/tests/torture_key.c
+++ b/tests/torture_key.c
@@ -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:
diff --git a/tests/unittests/torture_pki_ed25519.c b/tests/unittests/torture_pki_ed25519.c
index 3d486965..07ccfd67 100644
--- a/tests/unittests/torture_pki_ed25519.c
+++ b/tests/unittests/torture_pki_ed25519.c
@@ -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),