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-24 16:49:35 +0200
commita3a0529b41e5ce4789cc8a5bd5e09b4ed15efe32 (patch)
tree25a31744b2bdffad775c6329a6dd6c7da0ef6adb
parent61e6b6cc59eb18ed2a226974eed2da9e51e9db88 (diff)
downloadlibssh-a3a0529b41e5ce4789cc8a5bd5e09b4ed15efe32.tar.gz
libssh-a3a0529b41e5ce4789cc8a5bd5e09b4ed15efe32.tar.xz
libssh-a3a0529b41e5ce4789cc8a5bd5e09b4ed15efe32.zip
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>
-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),