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 @@ -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; } 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),