Compare commits

..

7 Commits

Author SHA1 Message Date
Praneeth Sarode
cc667021e5 tests(pki): add torture tests for security keys
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-08-01 15:28:58 +05:30
Praneeth Sarode
f9f8c939bc tests(pki): add security key testing helper functions to torture library
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-08-01 15:28:54 +05:30
Praneeth Sarode
aab6ce364a tests(pki): add sk type keys to the testing infrastructure
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-08-01 15:28:41 +05:30
Praneeth Sarode
0cec257077 pki: add security key file import/export functionality
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-08-01 15:28:36 +05:30
Praneeth Sarode
957efe51a2 format(pki): format the pki_import_privkey_buffer function
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-08-01 00:29:27 +05:30
Praneeth Sarode
bb85492d4f feat(pki): add support for SK key types in signature handling
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-07-30 23:00:53 +05:30
Praneeth Sarode
22c1b6970c pki: add security key fields to ssh_key_struct and update compare, copying and cleaning functions
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-07-30 22:47:59 +05:30
13 changed files with 1753 additions and 198 deletions

View File

@@ -86,6 +86,11 @@ struct ssh_key_struct {
ssh_string sk_application;
ssh_buffer cert;
enum ssh_keytypes_e cert_type;
/* Security Key specific private data */
uint8_t sk_flags;
ssh_string sk_key_handle;
ssh_string sk_reserved;
};
struct ssh_signature_struct {
@@ -137,6 +142,11 @@ enum ssh_digest_e ssh_key_hash_from_name(const char *name);
((kt) >= SSH_KEYTYPE_ECDSA_P256_CERT01 &&\
(kt) <= SSH_KEYTYPE_ED25519_CERT01))
#define is_sk_key_type(kt) \
((kt) == SSH_KEYTYPE_SK_ECDSA || (kt) == SSH_KEYTYPE_SK_ED25519 || \
(kt) == SSH_KEYTYPE_SK_ECDSA_CERT01 || \
(kt) == SSH_KEYTYPE_SK_ED25519_CERT01)
/* SSH Signature Functions */
ssh_signature ssh_signature_new(void);
void ssh_signature_free(ssh_signature sign);

View File

@@ -61,6 +61,7 @@ enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
enum ssh_keytypes_e type);
/* SSH Key Functions */
ssh_key pki_key_dup_common_init(const ssh_key key, int demote);
ssh_key pki_key_dup(const ssh_key key, int demote);
int pki_key_generate_rsa(ssh_key key, int parameter);
int pki_key_generate_ecdsa(ssh_key key, int parameter);
@@ -163,6 +164,11 @@ int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type);
#endif /* WITH_PKCS11_URI */
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
}
#endif

489
src/pki.c
View File

@@ -116,6 +116,78 @@ ssh_key ssh_key_new (void)
return ptr;
}
/**
* @internal
*
* @brief Initialize a new SSH key by duplicating common fields from an existing
* key.
*
* This function creates a new SSH key and copies the common fields from the
* source key, including the key type, type string, flags, and security key
* fields if applicable. This is a helper function used by key duplication
* routines.
*
* @param[in] key The source ssh_key to copy common fields from.
* @param[in] demote Whether to demote the new key to public only. If non-zero,
* only the public fields will be copied and the flags will
* be set accordingly.
*
* @return A new ssh_key with common fields initialized, or NULL on
* error.
*
* @note The caller is responsible for freeing the returned key with
* ssh_key_free().
*/
ssh_key pki_key_dup_common_init(const ssh_key key, int demote)
{
ssh_key new = NULL;
if (key == NULL) {
return NULL;
}
new = ssh_key_new();
if (new == NULL) {
return NULL;
}
new->type = key->type;
new->type_c = key->type_c;
if (demote) {
new->flags = SSH_KEY_FLAG_PUBLIC;
} else {
new->flags = key->flags;
}
/* Copy security key fields if present */
if (is_sk_key_type(key->type)) {
new->sk_application = ssh_string_copy(key->sk_application);
if (new->sk_application == NULL) {
goto fail;
}
if (!demote) {
new->sk_flags = key->sk_flags;
new->sk_key_handle = ssh_string_copy(key->sk_key_handle);
if (new->sk_key_handle == NULL) {
goto fail;
}
new->sk_reserved = ssh_string_copy(key->sk_reserved);
if (new->sk_reserved == NULL) {
goto fail;
}
}
}
return new;
fail:
SSH_KEY_FREE(new);
return NULL;
}
/**
* @brief duplicates the key
*
@@ -153,12 +225,14 @@ void ssh_key_clean (ssh_key key)
if (key->cert != NULL) {
SSH_BUFFER_FREE(key->cert);
}
if (key->type == SSH_KEYTYPE_SK_ECDSA ||
key->type == SSH_KEYTYPE_SK_ED25519 ||
key->type == SSH_KEYTYPE_SK_ECDSA_CERT01 ||
key->type == SSH_KEYTYPE_SK_ED25519_CERT01) {
if (is_sk_key_type(key->type)) {
ssh_string_burn(key->sk_application);
ssh_string_free(key->sk_application);
ssh_string_burn(key->sk_key_handle);
ssh_string_free(key->sk_key_handle);
ssh_string_burn(key->sk_reserved);
ssh_string_free(key->sk_reserved);
key->sk_flags = 0;
}
key->cert_type = SSH_KEYTYPE_UNKNOWN;
key->flags = SSH_KEY_FLAG_EMPTY;
@@ -432,6 +506,7 @@ static enum ssh_digest_e key_type_to_hash(enum ssh_keytypes_e type)
return SSH_DIGEST_SHA512;
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_SK_ECDSA:
return SSH_DIGEST_SHA256;
case SSH_KEYTYPE_ECDSA_P384_CERT01:
case SSH_KEYTYPE_ECDSA_P384:
@@ -441,6 +516,7 @@ static enum ssh_digest_e key_type_to_hash(enum ssh_keytypes_e type)
return SSH_DIGEST_SHA512;
case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
return SSH_DIGEST_AUTO;
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_DSS: /* deprecated */
@@ -706,13 +782,24 @@ int ssh_key_cmp(const ssh_key k1,
}
}
if (k1->type == SSH_KEYTYPE_SK_ECDSA ||
k1->type == SSH_KEYTYPE_SK_ED25519) {
if (strncmp(ssh_string_get_char(k1->sk_application),
ssh_string_get_char(k2->sk_application),
ssh_string_len(k2->sk_application)) != 0) {
if (is_sk_key_type(k1->type)) {
if (ssh_string_cmp(k1->sk_application, k2->sk_application) != 0) {
return 1;
}
if (what == SSH_KEY_CMP_PRIVATE) {
if (k1->sk_flags != k2->sk_flags) {
return 1;
}
if (ssh_string_cmp(k1->sk_key_handle, k2->sk_key_handle) != 0) {
return 1;
}
if (ssh_string_cmp(k1->sk_reserved, k2->sk_reserved) != 0) {
return 1;
}
}
}
if (what == SSH_KEY_CMP_CERTIFICATE) {
@@ -732,9 +819,10 @@ int ssh_key_cmp(const ssh_key k1,
}
#ifndef HAVE_LIBCRYPTO
if (k1->type == SSH_KEYTYPE_ED25519 ||
k1->type == SSH_KEYTYPE_SK_ED25519) {
if (k1->type == SSH_KEYTYPE_ED25519) {
return pki_ed25519_key_cmp(k1, k2, what);
} else if (k1->type == SSH_KEYTYPE_SK_ED25519) {
return pki_ed25519_key_cmp(k1, k2, SSH_KEY_CMP_PUBLIC);
}
#endif
@@ -909,10 +997,15 @@ ssh_pki_export_privkey_base64_format(const ssh_key privkey,
/*
* For historic reasons, the Ed25519 keys are exported in OpenSSH file
* 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
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;
}
#endif /* HAVE_LIBCRYPTO */
@@ -1136,10 +1229,16 @@ ssh_pki_export_privkey_file_format(const ssh_key privkey,
/*
* For historic reasons, the Ed25519 keys are exported in OpenSSH file
* 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
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;
}
#endif /* HAVE_LIBCRYPTO */
@@ -1288,129 +1387,178 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
switch (type) {
case SSH_KEYTYPE_RSA:
{
ssh_string n = NULL;
ssh_string e = NULL;
ssh_string d = NULL;
ssh_string iqmp = NULL;
ssh_string p = NULL;
ssh_string q = NULL;
case SSH_KEYTYPE_RSA: {
ssh_string n = NULL;
ssh_string e = NULL;
ssh_string d = NULL;
ssh_string iqmp = NULL;
ssh_string p = NULL;
ssh_string q = NULL;
rc = ssh_buffer_unpack(buffer, "SSSSSS", &n, &e, &d,
&iqmp, &p, &q);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
rc = pki_privkey_build_rsa(key, n, e, d, iqmp, p, q);
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("n", ssh_string_data(n), ssh_string_len(n));
ssh_log_hexdump("e", ssh_string_data(e), ssh_string_len(e));
ssh_log_hexdump("d", ssh_string_data(d), ssh_string_len(d));
ssh_log_hexdump("iqmp",
ssh_string_data(iqmp),
ssh_string_len(iqmp));
ssh_log_hexdump("p", ssh_string_data(p), ssh_string_len(p));
ssh_log_hexdump("q", ssh_string_data(q), ssh_string_len(q));
#endif /* DEBUG_CRYPTO */
ssh_string_burn(n);
SSH_STRING_FREE(n);
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(d);
SSH_STRING_FREE(d);
ssh_string_burn(iqmp);
SSH_STRING_FREE(iqmp);
ssh_string_burn(p);
SSH_STRING_FREE(p);
ssh_string_burn(q);
SSH_STRING_FREE(q);
if (rc == SSH_ERROR) {
SSH_LOG(SSH_LOG_TRACE, "Failed to build RSA private key");
goto fail;
}
}
break;
#ifdef HAVE_ECC
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
{
ssh_string e = NULL;
ssh_string exp = NULL;
ssh_string i = NULL;
int nid;
rc = ssh_buffer_unpack(buffer, "SSS", &i, &e, &exp);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
nid = pki_key_ecdsa_nid_from_name(ssh_string_get_char(i));
SSH_STRING_FREE(i);
if (nid == -1) {
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(exp);
SSH_STRING_FREE(exp);
goto fail;
}
rc = pki_privkey_build_ecdsa(key, nid, e, exp);
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(exp);
SSH_STRING_FREE(exp);
if (rc < 0) {
SSH_LOG(SSH_LOG_TRACE, "Failed to build ECDSA private key");
goto fail;
}
}
break;
#endif /* HAVE_ECC */
case SSH_KEYTYPE_ED25519:
{
ssh_string pubkey = NULL, privkey = NULL;
if (ssh_fips_mode()) {
SSH_LOG(SSH_LOG_TRACE,
"Ed25519 keys not supported in FIPS mode");
goto fail;
}
rc = ssh_buffer_unpack(buffer, "SS", &pubkey, &privkey);
if (rc != SSH_OK){
SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
rc = pki_privkey_build_ed25519(key, pubkey, privkey);
ssh_string_burn(privkey);
SSH_STRING_FREE(privkey);
SSH_STRING_FREE(pubkey);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_TRACE, "Failed to build ed25519 key");
goto fail;
}
}
break;
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
case SSH_KEYTYPE_ECDSA_P521_CERT01:
case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ECDSA_CERT01:
case SSH_KEYTYPE_SK_ED25519:
case SSH_KEYTYPE_SK_ED25519_CERT01:
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
SSH_LOG(SSH_LOG_TRACE, "Unknown private key type (%d)", type);
rc = ssh_buffer_unpack(buffer, "SSSSSS", &n, &e, &d, &iqmp, &p, &q);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
rc = pki_privkey_build_rsa(key, n, e, d, iqmp, p, q);
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("n", ssh_string_data(n), ssh_string_len(n));
ssh_log_hexdump("e", ssh_string_data(e), ssh_string_len(e));
ssh_log_hexdump("d", ssh_string_data(d), ssh_string_len(d));
ssh_log_hexdump("iqmp", ssh_string_data(iqmp), ssh_string_len(iqmp));
ssh_log_hexdump("p", ssh_string_data(p), ssh_string_len(p));
ssh_log_hexdump("q", ssh_string_data(q), ssh_string_len(q));
#endif /* DEBUG_CRYPTO */
ssh_string_burn(n);
SSH_STRING_FREE(n);
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(d);
SSH_STRING_FREE(d);
ssh_string_burn(iqmp);
SSH_STRING_FREE(iqmp);
ssh_string_burn(p);
SSH_STRING_FREE(p);
ssh_string_burn(q);
SSH_STRING_FREE(q);
if (rc == SSH_ERROR) {
SSH_LOG(SSH_LOG_TRACE, "Failed to build RSA private key");
goto fail;
}
break;
}
#ifdef HAVE_ECC
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521: {
ssh_string e = NULL;
ssh_string exp = NULL;
ssh_string i = NULL;
int nid;
rc = ssh_buffer_unpack(buffer, "SSS", &i, &e, &exp);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
nid = pki_key_ecdsa_nid_from_name(ssh_string_get_char(i));
SSH_STRING_FREE(i);
if (nid == -1) {
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(exp);
SSH_STRING_FREE(exp);
goto fail;
}
rc = pki_privkey_build_ecdsa(key, nid, e, exp);
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(exp);
SSH_STRING_FREE(exp);
if (rc < 0) {
SSH_LOG(SSH_LOG_TRACE, "Failed to build ECDSA private key");
goto fail;
}
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 */
case SSH_KEYTYPE_ED25519: {
ssh_string pubkey = NULL, privkey = NULL;
if (ssh_fips_mode()) {
SSH_LOG(SSH_LOG_TRACE, "Ed25519 keys not supported in FIPS mode");
goto fail;
}
rc = ssh_buffer_unpack(buffer, "SS", &pubkey, &privkey);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
rc = pki_privkey_build_ed25519(key, pubkey, privkey);
ssh_string_burn(privkey);
SSH_STRING_FREE(privkey);
SSH_STRING_FREE(pubkey);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_TRACE, "Failed to build ed25519 key");
goto fail;
}
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_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
case SSH_KEYTYPE_ECDSA_P521_CERT01:
case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_SK_ECDSA_CERT01:
case SSH_KEYTYPE_SK_ED25519_CERT01:
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
SSH_LOG(SSH_LOG_TRACE, "Unknown private key type (%d)", type);
goto fail;
}
*pkey = key;
@@ -2151,6 +2299,64 @@ int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey,
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
*
@@ -2422,6 +2628,15 @@ int ssh_pki_export_signature_blob(const ssh_signature sig,
return SSH_ERROR;
}
if (is_sk_key_type(sig->type)) {
/* Add flags and counter for SK keys */
rc = ssh_buffer_pack(buf, "bd", sig->sk_flags, sig->sk_counter);
if (rc < 0) {
SSH_BUFFER_FREE(buf);
return SSH_ERROR;
}
}
str = ssh_string_new(ssh_buffer_get_len(buf));
if (str == NULL) {
SSH_BUFFER_FREE(buf);
@@ -2635,11 +2850,7 @@ int ssh_pki_signature_verify(ssh_session session,
return SSH_ERROR;
}
if (key->type == SSH_KEYTYPE_SK_ECDSA ||
key->type == SSH_KEYTYPE_SK_ECDSA_CERT01 ||
key->type == SSH_KEYTYPE_SK_ED25519 ||
key->type == SSH_KEYTYPE_SK_ED25519_CERT01) {
if (is_sk_key_type(key->type)) {
ssh_buffer sk_buffer = NULL;
SHA256CTX ctx = NULL;
unsigned char application_hash[SHA256_DIGEST_LEN] = {0};

View File

@@ -485,19 +485,11 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
ssh_key new = NULL;
int rc;
new = ssh_key_new();
new = pki_key_dup_common_init(key, demote);
if (new == NULL) {
return NULL;
}
new->type = key->type;
new->type_c = key->type_c;
if (demote) {
new->flags = SSH_KEY_FLAG_PUBLIC;
} else {
new->flags = key->flags;
}
switch (key->type) {
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1: {
@@ -646,6 +638,7 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
case SSH_KEYTYPE_SK_ECDSA:
#ifdef HAVE_OPENSSL_ECC
new->ecdsa_nid = key->ecdsa_nid;
#ifdef WITH_PKCS11_URI
@@ -715,7 +708,8 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
#endif /* OPENSSL_VERSION_NUMBER */
break;
#endif /* HAVE_OPENSSL_ECC */
case SSH_KEYTYPE_ED25519: {
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519: {
#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* Take the PKCS#11 keys as they are */
if (key->flags & SSH_KEY_FLAG_PKCS11_URI && !demote) {
@@ -727,7 +721,8 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
return new;
}
if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE) &&
key->type == SSH_KEYTYPE_ED25519) {
unsigned char *ed25519_privkey = NULL;
size_t key_len = 0;
@@ -1019,7 +1014,7 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
return 1;
}
if (what == SSH_KEY_CMP_PRIVATE) {
if (what == SSH_KEY_CMP_PRIVATE && !is_sk_key_type(k1->type)) {
if (bignum_cmp(EC_KEY_get0_private_key(ec1),
EC_KEY_get0_private_key(ec2))) {
return 1;
@@ -1790,7 +1785,7 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
goto fail;
}
if (type == SSH_KEY_PRIVATE) {
if (type == SSH_KEY_PRIVATE && key->type == SSH_KEYTYPE_ED25519) {
key_len = 0;
rc = EVP_PKEY_get_raw_private_key(key->key, NULL, &key_len);
if (rc != 1) {
@@ -1836,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);
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 */
if (key->type == SSH_KEYTYPE_SK_ED25519) {
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
if (rc < 0) {
goto fail;
}
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
if (rc != SSH_OK) {
goto fail;
}
}
SAFE_FREE(ed25519_pubkey);
break;
case SSH_KEYTYPE_ECDSA_P256:
@@ -1949,7 +1951,8 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
ssh_string_burn(e);
SSH_STRING_FREE(e);
e = NULL;
if (type == SSH_KEY_PRIVATE) {
if (type == SSH_KEY_PRIVATE && key->type != SSH_KEYTYPE_SK_ECDSA) {
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_PARAM_free(params);
rc = EVP_PKEY_todata(key->key, EVP_PKEY_KEYPAIR, &params);
@@ -1986,10 +1989,18 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
ssh_string_burn(d);
SSH_STRING_FREE(d);
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 */
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
if (rc < 0) {
if (rc != SSH_OK) {
goto fail;
}
}
@@ -2163,6 +2174,11 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
sig_blob = pki_ecdsa_signature_to_blob(sig);
break;
#endif /* HAVE_OPENSSL_ECC */
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ED25519:
/* For SK keys, signature data is already in raw_sig */
sig_blob = ssh_string_copy(sig->raw_sig);
break;
default:
case SSH_KEYTYPE_UNKNOWN:
SSH_LOG(SSH_LOG_TRACE, "Unknown signature key type: %s", sig->type_c);

View File

@@ -1155,17 +1155,10 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
gcry_sexp_t curve = NULL;
new = ssh_key_new();
new = pki_key_dup_common_init(key, demote);
if (new == NULL) {
return NULL;
}
new->type = key->type;
new->type_c = key->type_c;
if (demote) {
new->flags = SSH_KEY_FLAG_PUBLIC;
} else {
new->flags = key->flags;
}
switch (key->type) {
case SSH_KEYTYPE_RSA:
@@ -1203,6 +1196,7 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
}
break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
rc = pki_ed25519_key_dup(new, key);
if (rc != SSH_OK) {
ssh_key_free(new);
@@ -1213,6 +1207,7 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
case SSH_KEYTYPE_SK_ECDSA:
#ifdef HAVE_GCRYPT_ECC
new->ecdsa_nid = key->ecdsa_nid;
@@ -1226,7 +1221,8 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
break;
}
if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE) &&
!is_sk_key_type(key->type)) {
err = gcry_sexp_build(&new->ecdsa,
NULL,
"(private-key(ecdsa %S (d %m)(q %m)))",
@@ -1427,7 +1423,7 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
return 1;
}
if (what == SSH_KEY_CMP_PRIVATE) {
if (what == SSH_KEY_CMP_PRIVATE && !is_sk_key_type(k1->type)) {
if (_bignum_cmp(k1->ecdsa, k2->ecdsa, "d") != 0) {
return 1;
}
@@ -1604,9 +1600,21 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
}
}
} else {
rc = pki_ed25519_private_key_to_blob(buffer, key);
if (rc == SSH_ERROR) {
goto fail;
if (key->type == SSH_KEYTYPE_SK_ED25519) {
rc = pki_ed25519_public_key_to_blob(buffer, key);
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;
@@ -1644,7 +1652,7 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
SSH_STRING_FREE(e);
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",
GCRYMPI_FMT_STD,
@@ -1661,7 +1669,14 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
ssh_string_burn(d);
SSH_STRING_FREE(d);
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 */
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
if (rc < 0) {
@@ -1813,6 +1828,11 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
break;
}
#endif
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ED25519:
/* For SK keys, signature data is already in raw_sig */
sig_blob = ssh_string_copy(sig->raw_sig);
break;
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:

View File

@@ -380,19 +380,11 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
mbedtls_mpi Q;
#endif
new = ssh_key_new();
new = pki_key_dup_common_init(key, demote);
if (new == NULL) {
return NULL;
}
new->type = key->type;
new->type_c = key->type_c;
if (demote) {
new->flags = SSH_KEY_FLAG_PUBLIC;
} else {
new->flags = key->flags;
}
#if MBEDTLS_VERSION_MAJOR > 2
mbedtls_mpi_init(&N);
mbedtls_mpi_init(&E);
@@ -512,6 +504,7 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
case SSH_KEYTYPE_SK_ECDSA:
new->ecdsa_nid = key->ecdsa_nid;
new->ecdsa = malloc(sizeof(mbedtls_ecdsa_context));
@@ -522,7 +515,8 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
mbedtls_ecdsa_init(new->ecdsa);
if (demote && ssh_key_is_private(key)) {
if ((demote && ssh_key_is_private(key)) ||
is_sk_key_type(key->type)) {
rc = mbedtls_ecp_copy(&new->ecdsa->MBEDTLS_PRIVATE(Q),
&key->ecdsa->MBEDTLS_PRIVATE(Q));
if (rc != 0) {
@@ -540,6 +534,7 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
rc = pki_ed25519_key_dup(new, key);
if (rc != SSH_OK) {
goto fail;
@@ -768,7 +763,8 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
goto cleanup;
}
if (what == SSH_KEY_CMP_PRIVATE) {
if (what == SSH_KEY_CMP_PRIVATE &&
k1->type != SSH_KEYTYPE_SK_ECDSA) {
if (mbedtls_mpi_cmp_mpi(&ecdsa1->MBEDTLS_PRIVATE(d),
&ecdsa2->MBEDTLS_PRIVATE(d)))
{
@@ -1070,7 +1066,7 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
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));
if (d == NULL) {
@@ -1082,7 +1078,14 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
if (rc < 0) {
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 */
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
if (rc < 0) {
@@ -1105,9 +1108,21 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
}
}
} else {
rc = pki_ed25519_private_key_to_blob(buffer, key);
if (rc == SSH_ERROR) {
goto out;
if (key->type == SSH_KEYTYPE_SK_ED25519) {
rc = pki_ed25519_public_key_to_blob(buffer, key);
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;
@@ -1219,6 +1234,11 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
case SSH_KEYTYPE_ED25519:
sig_blob = pki_ed25519_signature_to_blob(sig);
break;
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ED25519:
/* For SK keys, signature data is already in raw_sig */
sig_blob = ssh_string_copy(sig->raw_sig);
break;
default:
SSH_LOG(SSH_LOG_TRACE, "Unknown signature key type: %s",
sig->type_c);

View File

@@ -37,6 +37,7 @@ add_library(${TORTURE_LIBRARY}
torture.c
torture_key.c
torture_pki.c
torture_sk.c
torture_cmocka.c)
target_link_libraries(${TORTURE_LIBRARY} PRIVATE ${TORTURE_LINK_LIBRARIES})
target_compile_options(${TORTURE_LIBRARY} PRIVATE
@@ -77,6 +78,7 @@ if (CLIENT_TESTING)
torture.c
torture_key.c
torture_pki.c
torture_sk.c
torture_cmocka.c
)
target_link_libraries(${TORTURE_SHARED_LIBRARY} PUBLIC

View File

@@ -700,6 +700,37 @@ static const char torture_ecdsa521_testkey_cert[] =
"PKJ0pHVLZEx3FMZI3SfwS9mVm+oojLkZ2hr8X0xn28zbN045d8daB7BB1mHMGNT+YA"
"= ./ec521.pub";
static const char torture_ecdsa_sk_private_openssh_testkey[] =
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAfwAAACJzay1lY2\n"
"RzYS1zaGEyLW5pc3RwMjU2QG9wZW5zc2guY29tAAAACG5pc3RwMjU2AAAAQQRUfa1IVvak\n"
"xFQZudDtXVlTtw6SiuAgfTpqZBuMdcK55kgy3o7V2z02/XClN1zpvSydzdjGWVgLj6WE9Q\n"
"6xEOhQAAAABHNzaDoAAADoWSfkhlkn5IYAAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBv\n"
"cGVuc3NoLmNvbQAAAAhuaXN0cDI1NgAAAEEEVH2tSFb2pMRUGbnQ7V1ZU7cOkorgIH06am\n"
"QbjHXCueZIMt6O1ds9Nv1wpTdc6b0snc3YxllYC4+lhPUOsRDoUAAAAARzc2g6AQAAAEBS\n"
"Smuf/sZP2WxVdlqgSMN7E8VLFdZI717mTi/svHahGy3wcFp2tPPylCaIG9aKAQrfVt+pOJ\n"
"U+OPsm8rphRRM1AAAAAAAAABJwaG9lbml4QHBob2VuaXgtcGMBAg==\n"
"-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ecdsa_sk_private_openssh_testkey_passphrase[] =
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
"b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCzS672qr\n"
"+0DRopx7VjkjCnAAAAGAAAAAEAAAB/AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3Bl\n"
"bnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBFR9rUhW9qTEVBm50O1dWVO3DpKK4CB9OmpkG4\n"
"x1wrnmSDLejtXbPTb9cKU3XOm9LJ3N2MZZWAuPpYT1DrEQ6FAAAAAEc3NoOgAAAPDoWSmM\n"
"ki/XGLXidNxyLy2uRGejaZTOI3Ran10b7UF2ddRCrmBc6eVEXzgJ+BzB0sO0/uc1Q7QJhy\n"
"fGR9bz1rvwJd5RpLLw9cSoTHbDiap4tkQu2snQt7AF/E6MOgQ3mvdhDDYoTYvxNIiwZTH1\n"
"/Cxl2ZcRBKwSl6yp3JOxIVgttmJmNTqpt2U/uYwag9N1o6wxhWy1aamKZd1qHtPVC7MPL8\n"
"/Q96mBlCEIe3vd4Hge4wgDa24F4Lwat7IA0/NGNFISIQH7x4VaGHAiTeMFL1NOVyw52xWr\n"
"aAgXfkyplffxlB7ZfCf7RLsiCZDinMCE9y8=\n"
"-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ecdsa_sk_public_testkey[] =
"sk-ecdsa-sha2-nistp256@openssh.com "
"AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBF"
"R9rUhW9qTEVBm50O1dWVO3DpKK4CB9OmpkG4x1wrnmSDLejtXbPTb9cKU3XOm9LJ3N2MZZWAuP"
"pYT1DrEQ6FAAAAAEc3NoOg== phoenix@phoenix-pc";
/****************************************************************************
* ED25519 KEYS
****************************************************************************/
@@ -757,6 +788,35 @@ static const char torture_ed25519_testkey_cert[] =
"d8AogGWM6njfejbazFVyfnjNiWqatx6IV3Nnqc3LjCiPY19fqIPe2YJSzytHwLTD5X"
"IjD2bJpq2ZfjQwXpO0J ./ed.pub";
static const char torture_ed25519_sk_private_openssh_testkey[] =
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAABpzay1zc2\n"
"gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAACCihqLlueARJOQCZMYRHefNkQ3WBHlhlUOuG7a/\n"
"ivCkaQAAAARzc2g6AAAA+OivRKLor0SiAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY2\n"
"9tAAAAIKKGouW54BEk5AJkxhEd582RDdYEeWGVQ64btr+K8KRpAAAABHNzaDoBAAAAgNxc\n"
"Q6pfw2S2fpCEB1UGO4Fy8O5gXZDbw3Vj8EHTcUDucNmk/iaI/GTPcUQK5cgPJH8AaB+lIZ\n"
"GasyHd28mghgpaztG2cYmxrF3ZuvNdEZJecflgMOJDXZwoYvKpb7rZWjQgf8AeDy2u2dpl\n"
"XCKHH8/LkJHdo4MABojarKofgaGzAAAAAAAAABJwaG9lbml4QHBob2VuaXgtcGMBAgMEBQ\n"
"YH\n"
"-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ed25519_sk_private_openssh_testkey_passphrase[] =
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
"b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAkfYBhph\n"
"EvYRpuOO6V4wihAAAAGAAAAAEAAABKAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29t\n"
"AAAAIKKGouW54BEk5AJkxhEd582RDdYEeWGVQ64btr+K8KRpAAAABHNzaDoAAAEA2WmpuB\n"
"2ip0Bq4XJ9c2C33fE5twVYvK3WrJfAJKzih7bFXxbt5NmUFs121SD/x+3xZLwBJWGOIhdf\n"
"idOD4gy9VWWAGCdJ0v87T/WaBYzEACr32hd99cD+Ki7VmmAxOKxx2/+/gg+WkbgygNns3c\n"
"7YoYW5SSJm7WlhtmHFCKHtSh0fd8X1Q7gLHWTdd4B+3U9PyGpVgCKe2s2IOoTIcWOHlDW3\n"
"KbEdlKELKCUEb0kof5m3hu8cktn0J/YIe1Y98YVjv472P6CO0Jw92jHSEPiTGn8JdSPkBY\n"
"Qcoq18tszucoR2gp+sf5UvQhW8iOALDxO72Yq6HINAXNbpCB22U++GJw==\n"
"-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ed25519_sk_public_testkey[] =
"sk-ssh-ed25519@openssh.com "
"AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIKKGouW54BEk5AJkxhEd582RDdYEeW"
"GVQ64btr+K8KRpAAAABHNzaDo= phoenix@phoenix-pc";
static const char *
torture_get_testkey_public_internal(enum ssh_keytypes_e type,
enum torture_format_e format)
@@ -801,11 +861,19 @@ torture_get_testkey_public_internal(enum ssh_keytypes_e type,
return torture_ecdsa521_testkey_cert;
case SSH_KEYTYPE_ED25519_CERT01:
return torture_ed25519_testkey_cert;
case SSH_KEYTYPE_SK_ECDSA:
if (format == FORMAT_OPENSSH) {
return torture_ecdsa_sk_public_testkey;
}
return NULL;
case SSH_KEYTYPE_SK_ED25519:
if (format == FORMAT_OPENSSH) {
return torture_ed25519_sk_public_testkey;
}
return NULL;
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_ECDSA:
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ECDSA_CERT01:
case SSH_KEYTYPE_SK_ED25519:
case SSH_KEYTYPE_SK_ED25519_CERT01:
case SSH_KEYTYPE_UNKNOWN:
return NULL;
@@ -880,6 +948,26 @@ torture_get_testkey_encrypted_internal(enum ssh_keytypes_e type,
return NULL;
}
return NULL;
case SSH_KEYTYPE_SK_ECDSA:
switch (format) {
case FORMAT_OPENSSH:
return torture_ecdsa_sk_private_openssh_testkey_passphrase;
case FORMAT_PKCS8:
case FORMAT_PEM:
/* SK keys are not available in PKCS8 or PEM format */
return NULL;
}
return NULL;
case SSH_KEYTYPE_SK_ED25519:
switch (format) {
case FORMAT_OPENSSH:
return torture_ed25519_sk_private_openssh_testkey_passphrase;
case FORMAT_PKCS8:
case FORMAT_PEM:
/* SK keys are not available in PKCS8 or PEM format */
return NULL;
}
return NULL;
case SSH_KEYTYPE_DSS_CERT01:
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
@@ -888,9 +976,7 @@ torture_get_testkey_encrypted_internal(enum ssh_keytypes_e type,
case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_ECDSA:
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ECDSA_CERT01:
case SSH_KEYTYPE_SK_ED25519:
case SSH_KEYTYPE_SK_ED25519_CERT01:
case SSH_KEYTYPE_UNKNOWN:
return NULL;
@@ -965,6 +1051,24 @@ torture_get_testkey_internal(enum ssh_keytypes_e type,
return NULL;
}
return NULL;
case SSH_KEYTYPE_SK_ECDSA:
switch (format) {
case FORMAT_OPENSSH:
return torture_ecdsa_sk_private_openssh_testkey;
case FORMAT_PKCS8:
case FORMAT_PEM:
return NULL;
}
return NULL;
case SSH_KEYTYPE_SK_ED25519:
switch (format) {
case FORMAT_OPENSSH:
return torture_ed25519_sk_private_openssh_testkey;
case FORMAT_PKCS8:
case FORMAT_PEM:
return NULL;
}
return NULL;
case SSH_KEYTYPE_DSS_CERT01:
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
@@ -973,9 +1077,7 @@ torture_get_testkey_internal(enum ssh_keytypes_e type,
case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_ECDSA:
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ECDSA_CERT01:
case SSH_KEYTYPE_SK_ED25519:
case SSH_KEYTYPE_SK_ED25519_CERT01:
case SSH_KEYTYPE_UNKNOWN:
return NULL;

96
tests/torture_sk.c Normal file
View File

@@ -0,0 +1,96 @@
/*
* torture_sk.c - torture library for testing security keys
*
* This file is part of the SSH Library
*
* Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "torture_sk.h"
#include "libssh/pki.h"
#include "torture.h"
/* Helper function to validate ssh_key structure for security keys */
void assert_sk_key_valid(ssh_key key,
enum ssh_keytypes_e expected_type,
bool private)
{
char *app_str = NULL;
const char *expected_type_str = NULL;
assert_non_null(key);
assert_true(is_sk_key_type(expected_type));
assert_int_equal(key->type, expected_type);
if (private) {
assert_int_equal(key->flags,
SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC);
} else {
assert_int_equal(key->flags, SSH_KEY_FLAG_PUBLIC);
}
expected_type_str = ssh_key_type_to_char(expected_type);
assert_non_null(expected_type_str);
assert_non_null(key->type_c);
assert_string_equal(key->type_c, expected_type_str);
/* Validate security key specific fields */
assert_non_null(key->sk_application);
/* Validate application string format and content */
app_str = ssh_string_to_char(key->sk_application);
assert_non_null(app_str);
assert_true(ssh_string_len(key->sk_application) >= 4);
assert_true(strncmp(app_str, "ssh:", 4) == 0);
ssh_string_free_char(app_str);
if (private) {
assert_non_null(key->sk_key_handle);
assert_true(ssh_string_len(key->sk_key_handle) > 0);
}
/* TODO: Check for sk_flags */
/* Validate underlying cryptographic key exists based on type */
switch (expected_type) {
case SSH_KEYTYPE_SK_ECDSA:
#if defined(HAVE_LIBGCRYPT)
assert_non_null(key->ecdsa);
#elif defined(HAVE_LIBMBEDCRYPTO)
assert_non_null(key->ecdsa);
#elif defined(HAVE_LIBCRYPTO)
assert_non_null(key->key);
#endif
break;
case SSH_KEYTYPE_SK_ED25519:
#if defined(HAVE_LIBCRYPTO)
assert_non_null(key->key);
#elif !defined(HAVE_LIBCRYPTO)
assert_non_null(key->ed25519_pubkey);
#endif
break;
default:
/* Should not reach here */
assert_true(0);
break;
}
}

40
tests/torture_sk.h Normal file
View File

@@ -0,0 +1,40 @@
/*
* torture_sk.h - torture library for testing security keys
*
* This file is part of the SSH Library
*
* Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#ifndef _TORTURE_SK_H
#define _TORTURE_SK_H
#include "config.h"
#define LIBSSH_STATIC
#include <stdbool.h>
#include "torture.h"
#include "torture_pki.h"
void assert_sk_key_valid(ssh_key key,
enum ssh_keytypes_e expected_type,
bool private);
#endif /* _TORTURE_SK_H */

View File

@@ -47,6 +47,7 @@ if (UNIX AND NOT WIN32)
torture_pki_rsa
torture_pki_dsa
torture_pki_ed25519
torture_pki_sk_ed25519
torture_pki_sshsig
# requires /dev/null
torture_channel
@@ -91,6 +92,7 @@ if (UNIX AND NOT WIN32)
set(LIBSSH_UNIT_TESTS
${LIBSSH_UNIT_TESTS}
torture_pki_ecdsa
torture_pki_sk_ecdsa
)
endif()

View File

@@ -0,0 +1,487 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#define LIBSSH_STATIC
#include "pki.c"
#include "torture.h"
#include "torture_key.h"
#include "torture_pki.h"
#include "torture_sk.h"
/* Test constants */
#define LIBSSH_SK_ECDSA_TESTKEY "libssh_testkey.id_ecdsa_sk"
#define LIBSSH_SK_ECDSA_TESTKEY_PASSPHRASE \
"libssh_testkey_passphrase.id_ecdsa_sk"
const char template[] = "temp_dir_XXXXXX";
struct pki_st {
char *cwd;
char *temp_dir;
};
static int setup_sk_ecdsa_key(void **state)
{
const char *keystring = NULL;
struct pki_st *test_state = NULL;
char *cwd = NULL;
char *tmp_dir = NULL;
int rc = 0;
test_state = (struct pki_st *)malloc(sizeof(struct pki_st));
assert_non_null(test_state);
cwd = torture_get_current_working_dir();
assert_non_null(cwd);
tmp_dir = torture_make_temp_dir(template);
assert_non_null(tmp_dir);
test_state->cwd = cwd;
test_state->temp_dir = tmp_dir;
*state = test_state;
rc = torture_change_dir(tmp_dir);
assert_int_equal(rc, 0);
printf("Changed directory to: %s\n", tmp_dir);
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ECDSA, 0);
torture_write_file(LIBSSH_SK_ECDSA_TESTKEY, keystring);
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ECDSA, 1);
torture_write_file(LIBSSH_SK_ECDSA_TESTKEY_PASSPHRASE, keystring);
keystring = torture_get_testkey_pub(SSH_KEYTYPE_SK_ECDSA);
torture_write_file(LIBSSH_SK_ECDSA_TESTKEY ".pub", keystring);
return 0;
}
static int teardown(void **state)
{
struct pki_st *test_state = NULL;
int rc = 0;
test_state = *((struct pki_st **)state);
assert_non_null(test_state);
assert_non_null(test_state->cwd);
assert_non_null(test_state->temp_dir);
rc = torture_change_dir(test_state->cwd);
assert_int_equal(rc, 0);
rc = torture_rmdirs(test_state->temp_dir);
assert_int_equal(rc, 0);
SAFE_FREE(test_state->temp_dir);
SAFE_FREE(test_state->cwd);
SAFE_FREE(test_state);
return 0;
}
static void torture_pki_sk_ecdsa_import_pubkey_file(void **state)
{
ssh_key pubkey = NULL;
int rc;
(void)state; /* unused */
rc = ssh_pki_import_pubkey_file(LIBSSH_SK_ECDSA_TESTKEY ".pub", &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ECDSA, false);
SSH_KEY_FREE(pubkey);
}
static void
torture_pki_sk_ecdsa_import_pubkey_from_openssh_privkey(void **state)
{
ssh_key pubkey = NULL;
int rc;
(void)state; /* unused */
rc =
ssh_pki_import_pubkey_file(LIBSSH_SK_ECDSA_TESTKEY_PASSPHRASE, &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ECDSA, false);
SSH_KEY_FREE(pubkey);
}
static void torture_pki_sk_ecdsa_import_privkey_base64(void **state)
{
ssh_key privkey = NULL;
char *keystring = NULL;
int rc;
(void)state; /* unused */
keystring = torture_pki_read_file(LIBSSH_SK_ECDSA_TESTKEY);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring,
NULL, /* no passphrase */
NULL, /* no auth callback */
NULL, /* no auth data */
&privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ECDSA, true);
SAFE_FREE(keystring);
SSH_KEY_FREE(privkey);
}
static void torture_pki_sk_ecdsa_import_privkey_base64_comment(void **state)
{
int rc, file_str_len;
const char *comment_str = "#this is line-comment\n#this is another\n";
char *file_str = NULL;
ssh_key key = NULL;
char *keystring = NULL;
(void)state; /* unused */
keystring = torture_pki_read_file(LIBSSH_SK_ECDSA_TESTKEY);
assert_non_null(keystring);
file_str_len = strlen(comment_str) + strlen(keystring) + 1;
file_str = malloc(file_str_len);
assert_non_null(file_str);
rc = snprintf(file_str, file_str_len, "%s%s", comment_str, keystring);
assert_int_equal(rc, file_str_len - 1);
rc = ssh_pki_import_privkey_base64(file_str, NULL, NULL, NULL, &key);
assert_return_code(rc, errno);
assert_sk_key_valid(key, SSH_KEYTYPE_SK_ECDSA, true);
SAFE_FREE(keystring);
SAFE_FREE(file_str);
SSH_KEY_FREE(key);
}
static void torture_pki_sk_ecdsa_import_privkey_base64_whitespace(void **state)
{
int rc, file_str_len;
const char *whitespace_str = " \t\t\t\n\n\n";
char *file_str = NULL;
ssh_key key = NULL;
char *keystring = NULL;
(void)state; /* unused */
keystring = torture_pki_read_file(LIBSSH_SK_ECDSA_TESTKEY);
assert_non_null(keystring);
file_str_len = 2 * strlen(whitespace_str) + strlen(keystring) + 1;
file_str = malloc(file_str_len);
assert_non_null(file_str);
rc = snprintf(file_str,
file_str_len,
"%s%s%s",
whitespace_str,
keystring,
whitespace_str);
assert_int_equal(rc, file_str_len - 1);
rc = ssh_pki_import_privkey_base64(file_str, NULL, NULL, NULL, &key);
assert_return_code(rc, errno);
assert_sk_key_valid(key, SSH_KEYTYPE_SK_ECDSA, true);
SAFE_FREE(keystring);
SAFE_FREE(file_str);
SSH_KEY_FREE(key);
}
static void torture_pki_sk_ecdsa_import_export_privkey_base64(void **state)
{
ssh_key origkey = NULL;
ssh_key privkey = NULL;
char *key_buf = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ECDSA, 0);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring, NULL, NULL, NULL, &origkey);
assert_return_code(rc, errno);
assert_sk_key_valid(origkey, SSH_KEYTYPE_SK_ECDSA, true);
rc = ssh_pki_export_privkey_base64(origkey, NULL, NULL, NULL, &key_buf);
assert_return_code(rc, errno);
assert_non_null(key_buf);
rc = ssh_pki_import_privkey_base64(key_buf, NULL, NULL, NULL, &privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ECDSA, true);
rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
assert_int_equal(rc, 0);
SSH_KEY_FREE(origkey);
SSH_KEY_FREE(privkey);
SSH_STRING_FREE_CHAR(key_buf);
}
static void torture_pki_sk_ecdsa_publickey_from_privatekey(void **state)
{
ssh_key privkey = NULL;
ssh_key pubkey = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ECDSA, 0);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring,
NULL, /* no passphrase */
NULL, /* no auth callback */
NULL, /* no auth data */
&privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ECDSA, true);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ECDSA, false);
rc = ssh_key_cmp(privkey, pubkey, SSH_KEY_CMP_PUBLIC);
assert_int_equal(rc, 0);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
}
static void torture_pki_sk_ecdsa_import_privkey_base64_passphrase(void **state)
{
ssh_key privkey = NULL;
const char *keystring = NULL;
const char *passphrase = NULL;
int rc;
(void)state; /* unused */
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ECDSA, 1);
assert_non_null(keystring);
passphrase = torture_get_testkey_passphrase();
assert_non_null(passphrase);
/* Import with a passphrase */
rc = ssh_pki_import_privkey_base64(keystring,
passphrase,
NULL,
NULL,
&privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ECDSA, true);
SSH_KEY_FREE(privkey);
rc = ssh_pki_import_privkey_base64(keystring,
"wrong passphrase",
NULL,
NULL,
&privkey);
assert_int_equal(rc, SSH_ERROR);
assert_null(privkey);
}
static void torture_pki_sk_ecdsa_duplicate_key(void **state)
{
ssh_key privkey = NULL;
ssh_key duplicated = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ECDSA, 0);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring,
NULL, /* no passphrase */
NULL, /* no auth callback */
NULL, /* no auth data */
&privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ECDSA, true);
duplicated = ssh_key_dup(privkey);
assert_sk_key_valid(duplicated, SSH_KEYTYPE_SK_ECDSA, true);
rc = ssh_key_cmp(privkey, duplicated, SSH_KEY_CMP_PRIVATE);
assert_int_equal(rc, 0);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(duplicated);
}
static void torture_pki_sk_ecdsa_import_pubkey_base64(void **state)
{
ssh_key key = NULL;
ssh_key pubkey = NULL;
char *b64_key = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ECDSA, 0);
assert_non_null(keystring);
/* Import private key to extract public key */
rc = ssh_pki_import_privkey_base64(keystring, NULL, NULL, NULL, &key);
assert_return_code(rc, errno);
assert_sk_key_valid(key, SSH_KEYTYPE_SK_ECDSA, true);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ECDSA, false);
/* Export public key to base64 */
rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key);
assert_return_code(rc, errno);
assert_non_null(b64_key);
SSH_KEY_FREE(key);
SSH_KEY_FREE(pubkey);
/* Import public key from base64 */
rc = ssh_pki_import_pubkey_base64(b64_key, SSH_KEYTYPE_SK_ECDSA, &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ECDSA, false);
SSH_KEY_FREE(pubkey);
SSH_STRING_FREE_CHAR(b64_key);
}
static void torture_pki_sk_ecdsa_pubkey_blob(void **state)
{
ssh_key privkey = NULL;
ssh_key pubkey = NULL;
ssh_key imported_pubkey = NULL;
ssh_string pub_blob = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ECDSA, 0);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring, NULL, NULL, NULL, &privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ECDSA, true);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ECDSA, false);
/* Export public key to blob */
rc = ssh_pki_export_pubkey_blob(pubkey, &pub_blob);
assert_int_equal(rc, SSH_OK);
assert_non_null(pub_blob);
/* Import public key from blob */
rc = ssh_pki_import_pubkey_blob(pub_blob, &imported_pubkey);
assert_int_equal(rc, SSH_OK);
assert_sk_key_valid(imported_pubkey, SSH_KEYTYPE_SK_ECDSA, false);
/* Compare keys */
rc = ssh_key_cmp(pubkey, imported_pubkey, SSH_KEY_CMP_PUBLIC);
assert_int_equal(rc, 0);
ssh_string_free(pub_blob);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
SSH_KEY_FREE(imported_pubkey);
}
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_pki_sk_ecdsa_import_pubkey_file,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ecdsa_import_pubkey_from_openssh_privkey,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ecdsa_import_privkey_base64,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ecdsa_import_privkey_base64_comment,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ecdsa_import_privkey_base64_whitespace,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ecdsa_import_export_privkey_base64,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ecdsa_publickey_from_privatekey,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ecdsa_import_pubkey_base64,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ecdsa_import_privkey_base64_passphrase,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(torture_pki_sk_ecdsa_duplicate_key,
setup_sk_ecdsa_key,
teardown),
cmocka_unit_test_setup_teardown(torture_pki_sk_ecdsa_pubkey_blob,
setup_sk_ecdsa_key,
teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, NULL, NULL);
ssh_finalize();
return rc;
}

View File

@@ -0,0 +1,543 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#define LIBSSH_STATIC
#include "pki.c"
#include "torture.h"
#include "torture_key.h"
#include "torture_pki.h"
#include "torture_sk.h"
/* Test constants */
#define LIBSSH_SK_ED25519_TESTKEY "libssh_testkey.id_ed25519_sk"
#define LIBSSH_SK_ED25519_TESTKEY_PASSPHRASE \
"libssh_testkey_passphrase.id_ed25519_sk"
const char template[] = "temp_dir_XXXXXX";
struct pki_st {
char *cwd;
char *temp_dir;
};
static int setup_sk_ed25519_key(void **state)
{
const char *keystring = NULL;
struct pki_st *test_state = NULL;
char *cwd = NULL;
char *tmp_dir = NULL;
int rc = 0;
test_state = (struct pki_st *)malloc(sizeof(struct pki_st));
assert_non_null(test_state);
cwd = torture_get_current_working_dir();
assert_non_null(cwd);
tmp_dir = torture_make_temp_dir(template);
assert_non_null(tmp_dir);
test_state->cwd = cwd;
test_state->temp_dir = tmp_dir;
*state = test_state;
rc = torture_change_dir(tmp_dir);
assert_int_equal(rc, 0);
printf("Changed directory to: %s\n", tmp_dir);
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ED25519, 0);
torture_write_file(LIBSSH_SK_ED25519_TESTKEY, keystring);
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ED25519, 1);
torture_write_file(LIBSSH_SK_ED25519_TESTKEY_PASSPHRASE, keystring);
keystring = torture_get_testkey_pub(SSH_KEYTYPE_SK_ED25519);
torture_write_file(LIBSSH_SK_ED25519_TESTKEY ".pub", keystring);
return 0;
}
static int teardown(void **state)
{
struct pki_st *test_state = NULL;
int rc = 0;
test_state = *((struct pki_st **)state);
assert_non_null(test_state);
assert_non_null(test_state->cwd);
assert_non_null(test_state->temp_dir);
rc = torture_change_dir(test_state->cwd);
assert_int_equal(rc, 0);
rc = torture_rmdirs(test_state->temp_dir);
assert_int_equal(rc, 0);
SAFE_FREE(test_state->temp_dir);
SAFE_FREE(test_state->cwd);
SAFE_FREE(test_state);
return 0;
}
static void torture_pki_sk_ed25519_import_pubkey_file(void **state)
{
ssh_key pubkey = NULL;
int rc;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
rc = ssh_pki_import_pubkey_file(LIBSSH_SK_ED25519_TESTKEY ".pub", &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ED25519, false);
SSH_KEY_FREE(pubkey);
}
static void
torture_pki_sk_ed25519_import_pubkey_from_openssh_privkey(void **state)
{
ssh_key pubkey = NULL;
int rc;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
rc = ssh_pki_import_pubkey_file(LIBSSH_SK_ED25519_TESTKEY_PASSPHRASE,
&pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ED25519, false);
SSH_KEY_FREE(pubkey);
}
static void torture_pki_sk_ed25519_import_privkey_base64(void **state)
{
ssh_key privkey = NULL;
char *keystring = NULL;
int rc;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
keystring = torture_pki_read_file(LIBSSH_SK_ED25519_TESTKEY);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring,
NULL, /* no passphrase */
NULL, /* no auth callback */
NULL, /* no auth data */
&privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ED25519, true);
SAFE_FREE(keystring);
SSH_KEY_FREE(privkey);
}
static void torture_pki_sk_ed25519_import_privkey_base64_comment(void **state)
{
int rc, file_str_len;
const char *comment_str = "#this is line-comment\n#this is another\n";
char *file_str = NULL;
ssh_key key = NULL;
char *keystring = NULL;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
keystring = torture_pki_read_file(LIBSSH_SK_ED25519_TESTKEY);
assert_non_null(keystring);
file_str_len = strlen(comment_str) + strlen(keystring) + 1;
file_str = malloc(file_str_len);
assert_non_null(file_str);
rc = snprintf(file_str, file_str_len, "%s%s", comment_str, keystring);
assert_int_equal(rc, file_str_len - 1);
rc = ssh_pki_import_privkey_base64(file_str, NULL, NULL, NULL, &key);
assert_return_code(rc, errno);
assert_sk_key_valid(key, SSH_KEYTYPE_SK_ED25519, true);
SAFE_FREE(keystring);
SAFE_FREE(file_str);
SSH_KEY_FREE(key);
}
static void
torture_pki_sk_ed25519_import_privkey_base64_whitespace(void **state)
{
int rc, file_str_len;
const char *whitespace_str = " \t\t\t\n\n\n";
char *file_str = NULL;
ssh_key key = NULL;
char *keystring = NULL;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
keystring = torture_pki_read_file(LIBSSH_SK_ED25519_TESTKEY);
assert_non_null(keystring);
file_str_len = 2 * strlen(whitespace_str) + strlen(keystring) + 1;
file_str = malloc(file_str_len);
assert_non_null(file_str);
rc = snprintf(file_str,
file_str_len,
"%s%s%s",
whitespace_str,
keystring,
whitespace_str);
assert_int_equal(rc, file_str_len - 1);
rc = ssh_pki_import_privkey_base64(file_str, NULL, NULL, NULL, &key);
assert_return_code(rc, errno);
assert_sk_key_valid(key, SSH_KEYTYPE_SK_ED25519, true);
SAFE_FREE(keystring);
SAFE_FREE(file_str);
SSH_KEY_FREE(key);
}
static void torture_pki_sk_ed25519_import_export_privkey_base64(void **state)
{
ssh_key origkey = NULL;
ssh_key privkey = NULL;
char *key_buf = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ED25519, 0);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring, NULL, NULL, NULL, &origkey);
assert_return_code(rc, errno);
assert_sk_key_valid(origkey, SSH_KEYTYPE_SK_ED25519, true);
rc = ssh_pki_export_privkey_base64(origkey, NULL, NULL, NULL, &key_buf);
assert_return_code(rc, errno);
assert_non_null(key_buf);
rc = ssh_pki_import_privkey_base64(key_buf, NULL, NULL, NULL, &privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ED25519, true);
rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
assert_int_equal(rc, 0);
SSH_KEY_FREE(origkey);
SSH_KEY_FREE(privkey);
SSH_STRING_FREE_CHAR(key_buf);
}
static void torture_pki_sk_ed25519_publickey_from_privatekey(void **state)
{
ssh_key privkey = NULL;
ssh_key pubkey = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ED25519, 0);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring,
NULL, /* no passphrase */
NULL, /* no auth callback */
NULL, /* no auth data */
&privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ED25519, true);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ED25519, false);
rc = ssh_key_cmp(privkey, pubkey, SSH_KEY_CMP_PUBLIC);
assert_int_equal(rc, 0);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
}
static void
torture_pki_sk_ed25519_import_privkey_base64_passphrase(void **state)
{
ssh_key privkey = NULL;
const char *keystring = NULL;
const char *passphrase = NULL;
int rc;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ED25519, 1);
assert_non_null(keystring);
passphrase = torture_get_testkey_passphrase();
assert_non_null(passphrase);
/* Import with a passphrase */
rc = ssh_pki_import_privkey_base64(keystring,
passphrase,
NULL,
NULL,
&privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ED25519, true);
SSH_KEY_FREE(privkey);
rc = ssh_pki_import_privkey_base64(keystring,
"wrong passphrase",
NULL,
NULL,
&privkey);
assert_int_equal(rc, SSH_ERROR);
assert_null(privkey);
}
static void torture_pki_sk_ed25519_duplicate_key(void **state)
{
ssh_key privkey = NULL;
ssh_key duplicated = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ED25519, 0);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring,
NULL, /* no passphrase */
NULL, /* no auth callback */
NULL, /* no auth data */
&privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ED25519, true);
duplicated = ssh_key_dup(privkey);
assert_sk_key_valid(duplicated, SSH_KEYTYPE_SK_ED25519, true);
rc = ssh_key_cmp(privkey, duplicated, SSH_KEY_CMP_PRIVATE);
assert_int_equal(rc, 0);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(duplicated);
}
static void torture_pki_sk_ed25519_import_pubkey_base64(void **state)
{
ssh_key key = NULL;
ssh_key pubkey = NULL;
char *b64_key = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ED25519, 0);
assert_non_null(keystring);
/* Import private key to extract public key */
rc = ssh_pki_import_privkey_base64(keystring, NULL, NULL, NULL, &key);
assert_return_code(rc, errno);
assert_sk_key_valid(key, SSH_KEYTYPE_SK_ED25519, true);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ED25519, false);
/* Export public key to base64 */
rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key);
assert_return_code(rc, errno);
assert_non_null(b64_key);
SSH_KEY_FREE(key);
SSH_KEY_FREE(pubkey);
/* Import public key from base64 */
rc = ssh_pki_import_pubkey_base64(b64_key, SSH_KEYTYPE_SK_ED25519, &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ED25519, false);
SSH_KEY_FREE(pubkey);
SSH_STRING_FREE_CHAR(b64_key);
}
static void torture_pki_sk_ed25519_pubkey_blob(void **state)
{
ssh_key privkey = NULL;
ssh_key pubkey = NULL;
ssh_key imported_pubkey = NULL;
ssh_string pub_blob = NULL;
const char *keystring = NULL;
int rc;
(void)state; /* unused */
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
keystring = torture_get_openssh_testkey(SSH_KEYTYPE_SK_ED25519, 0);
assert_non_null(keystring);
rc = ssh_pki_import_privkey_base64(keystring, NULL, NULL, NULL, &privkey);
assert_return_code(rc, errno);
assert_sk_key_valid(privkey, SSH_KEYTYPE_SK_ED25519, true);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
assert_sk_key_valid(pubkey, SSH_KEYTYPE_SK_ED25519, false);
/* Export public key to blob */
rc = ssh_pki_export_pubkey_blob(pubkey, &pub_blob);
assert_int_equal(rc, SSH_OK);
assert_non_null(pub_blob);
/* Import public key from blob */
rc = ssh_pki_import_pubkey_blob(pub_blob, &imported_pubkey);
assert_int_equal(rc, SSH_OK);
assert_sk_key_valid(imported_pubkey, SSH_KEYTYPE_SK_ED25519, false);
/* Compare keys */
rc = ssh_key_cmp(pubkey, imported_pubkey, SSH_KEY_CMP_PUBLIC);
assert_int_equal(rc, 0);
ssh_string_free(pub_blob);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
SSH_KEY_FREE(imported_pubkey);
}
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(
torture_pki_sk_ed25519_import_pubkey_file,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ed25519_import_pubkey_from_openssh_privkey,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ed25519_import_privkey_base64,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ed25519_import_privkey_base64_comment,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ed25519_import_privkey_base64_whitespace,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ed25519_import_export_privkey_base64,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ed25519_publickey_from_privatekey,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ed25519_import_pubkey_base64,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(
torture_pki_sk_ed25519_import_privkey_base64_passphrase,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(torture_pki_sk_ed25519_duplicate_key,
setup_sk_ed25519_key,
teardown),
cmocka_unit_test_setup_teardown(torture_pki_sk_ed25519_pubkey_blob,
setup_sk_ed25519_key,
teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, NULL, NULL);
ssh_finalize();
return rc;
}