From 5937b5ba4e79634f0053c8bb9ecf6a743a519576 Mon Sep 17 00:00:00 2001 From: Praneeth Sarode Date: Thu, 23 Oct 2025 22:31:20 +0530 Subject: [PATCH] feat(torture_sk): add functions to validate security key signatures and to create PKI context Signed-off-by: Praneeth Sarode Reviewed-by: Jakub Jelen Reviewed-by: Eshan Kelkar --- tests/torture_sk.c | 145 +++++++++++++++++++++++++++++++++++++++++++++ tests/torture_sk.h | 50 +++++++++++++++- 2 files changed, 193 insertions(+), 2 deletions(-) diff --git a/tests/torture_sk.c b/tests/torture_sk.c index 40922538..fdb2a815 100644 --- a/tests/torture_sk.c +++ b/tests/torture_sk.c @@ -23,6 +23,7 @@ #include "torture_sk.h" #include "libssh/pki.h" +#include "libssh/pki_priv.h" #include "libssh/sk_api.h" /* For SSH_SK_* flag definitions */ void assert_sk_key_valid(ssh_key key, @@ -100,6 +101,150 @@ void assert_sk_key_valid(ssh_key key, } } +void assert_sk_signature_valid(ssh_signature signature, + enum ssh_keytypes_e expected_type, + ssh_key signing_key, + const uint8_t *data, + size_t data_len) +{ + uint8_t valid_flags; + const char *expected_type_str = NULL; + ssh_string sig_blob = NULL; + ssh_signature reconstructed = NULL; + ssh_buffer sk_sig_buffer = NULL; + int rc; + + /* Basic null and type validation */ + assert_non_null(signature); + assert_int_equal(signature->type, expected_type); + + /* Validate hash type is appropriate for security keys */ + switch (expected_type) { + case SSH_KEYTYPE_SK_ECDSA: + assert_int_equal(signature->hash_type, SSH_DIGEST_SHA256); + break; + case SSH_KEYTYPE_SK_ED25519: + assert_int_equal(signature->hash_type, SSH_DIGEST_AUTO); + break; + default: + /* Should not reach here */ + assert_true(0); + break; + } + + expected_type_str = ssh_key_type_to_char(expected_type); + assert_non_null(signature->type_c); + assert_string_equal(signature->type_c, expected_type_str); + + /* Check that only valid SK flags are set */ + valid_flags = SSH_SK_USER_PRESENCE_REQD | SSH_SK_USER_VERIFICATION_REQD; + assert_int_equal(signature->sk_flags & ~valid_flags, 0); + + assert_true(signature->sk_flags & SSH_SK_USER_PRESENCE_REQD); + assert_true(signature->sk_counter > 0); + + assert_non_null(signature->raw_sig); + assert_true(ssh_string_len(signature->raw_sig) > 0); + + rc = ssh_pki_export_signature_blob(signature, &sig_blob); + assert_int_equal(rc, SSH_OK); + assert_non_null(sig_blob); + + assert_non_null(signing_key); + rc = ssh_pki_import_signature_blob(sig_blob, signing_key, &reconstructed); + assert_int_equal(rc, SSH_OK); + assert_non_null(reconstructed); + + rc = pki_sk_signature_buffer_prepare(signing_key, + reconstructed, + data, + data_len, + &sk_sig_buffer); + assert_int_equal(rc, SSH_OK); + assert_non_null(sk_sig_buffer); + + rc = pki_verify_data_signature(reconstructed, + signing_key, + ssh_buffer_get(sk_sig_buffer), + ssh_buffer_get_len(sk_sig_buffer)); + assert_int_equal(rc, SSH_OK); + + SSH_BUFFER_FREE(sk_sig_buffer); + + ssh_signature_free(reconstructed); + ssh_string_free(sig_blob); +} + +ssh_pki_ctx +torture_create_sk_pki_ctx(const char *application, + uint8_t flags, + const void *challenge_data, + size_t challenge_len, + ssh_auth_callback pin_callback, + const char *device_path, + const char *user_id, + const struct ssh_sk_callbacks_struct *sk_callbacks) +{ + ssh_pki_ctx ctx = NULL; + ssh_buffer challenge_buffer = NULL; + int rc; + + ctx = ssh_pki_ctx_new(); + assert_non_null(ctx); + + rc = ssh_pki_ctx_options_set(ctx, + SSH_PKI_OPTION_SK_APPLICATION, + application); + assert_int_equal(rc, SSH_OK); + + rc = ssh_pki_ctx_options_set(ctx, SSH_PKI_OPTION_SK_FLAGS, &flags); + assert_int_equal(rc, SSH_OK); + + if (challenge_data != NULL && challenge_len > 0) { + challenge_buffer = ssh_buffer_new(); + assert_non_null(challenge_buffer); + + rc = ssh_buffer_add_data(challenge_buffer, + challenge_data, + challenge_len); + assert_int_equal(rc, SSH_OK); + } + + rc = ssh_pki_ctx_options_set(ctx, + SSH_PKI_OPTION_SK_CHALLENGE, + challenge_buffer); + assert_int_equal(rc, SSH_OK); + + SSH_BUFFER_FREE(challenge_buffer); + + rc = ssh_pki_ctx_set_sk_pin_callback(ctx, pin_callback, NULL); + assert_int_equal(rc, SSH_OK); + + if (device_path != NULL) { + rc = ssh_pki_ctx_sk_callbacks_option_set(ctx, + SSH_SK_OPTION_NAME_DEVICE_PATH, + device_path, + false); + assert_int_equal(rc, SSH_OK); + } + if (user_id != NULL) { + rc = ssh_pki_ctx_sk_callbacks_option_set(ctx, + SSH_SK_OPTION_NAME_USER_ID, + user_id, + false); + assert_int_equal(rc, SSH_OK); + } + + if (sk_callbacks != NULL) { + rc = ssh_pki_ctx_options_set(ctx, + SSH_PKI_OPTION_SK_CALLBACKS, + sk_callbacks); + assert_int_equal(rc, SSH_OK); + } + + return ctx; +} + void assert_sk_enroll_response(struct sk_enroll_response *response, int flags) { assert_non_null(response); diff --git a/tests/torture_sk.h b/tests/torture_sk.h index 535683e5..d63b02a0 100644 --- a/tests/torture_sk.h +++ b/tests/torture_sk.h @@ -28,9 +28,10 @@ #define LIBSSH_STATIC -#include "torture.h" - #include "libssh/callbacks.h" +#include "libssh/pki.h" +#include "torture.h" +#include "torture_pki.h" /** * @brief Validate a security key (ssh_key) structure @@ -46,6 +47,51 @@ void assert_sk_key_valid(ssh_key key, enum ssh_keytypes_e expected_type, bool private); +/** + * @brief Validate a security key signature structure + * + * Checks that the signature is not NULL, matches the expected key type, and + * other internal fields. Also verifies that the signature was produced by the + * given signing key. + * + * @param[in] signature The signature to validate + * @param[in] expected_type The expected key type (e.g., SSH_KEYTYPE_SK_ECDSA) + * @param[in] signing_key The key that should have produced the signature + * @param[in] data The signed data buffer + * @param[in] data_len Length of the signed data + */ +void assert_sk_signature_valid(ssh_signature signature, + enum ssh_keytypes_e expected_type, + ssh_key signing_key, + const uint8_t *data, + size_t data_len); + +/** + * @brief Create and initialize a PKI context configured for security key + * operations. + * + * Parameters: + * @param[in] application Application string + * @param[in] flags SK flags + * @param[in] challenge_data Optional challenge bytes (may be NULL) + * @param[in] challenge_len Length of challenge_data + * @param[in] pin_callback Callback used to obtain the PIN (may be NULL) + * @param[in] device_path Optional device path (may be NULL) + * @param[in] user_id Optional user_id string (may be NULL) + * @param[in] sk_callbacks Pointer to SK callbacks (may be NULL) + * + * @return A configured ssh_pki_ctx on success, or NULL on allocation failure. + */ +ssh_pki_ctx +torture_create_sk_pki_ctx(const char *application, + uint8_t flags, + const void *challenge_data, + size_t challenge_len, + ssh_auth_callback pin_callback, + const char *device_path, + const char *user_id, + const struct ssh_sk_callbacks_struct *sk_callbacks); + /** * @brief Validate a security key enrollment response structure *