Visitar URL original
openssl: check key type for sk-ecdsa in `ossl_key_from_openssh()` by abdul-khaliq-khalid · Pull Request #2672 · libssh2/libssh2 · GitHub
Skip to content

openssl: check key type for sk-ecdsa in ossl_key_from_openssh() - #2672

Open
abdul-khaliq-khalid wants to merge 5 commits into
libssh2:masterfrom
abdul-khaliq-khalid:openssl-sk-ecdsa-want-method
Open

abdul-khaliq-khalid wants to merge 5 commits into
libssh2:masterfrom
abdul-khaliq-khalid:openssl-sk-ecdsa-want-method

Conversation

@abdul-khaliq-khalid

Copy link
Copy Markdown
Contributor

In ossl_key_from_openssh() the sk-ecdsa-sha2-nistp256@openssh.com branch is the only one that does not check want_method, so ssh2_rsa_new_priv(), ssh2_dsa_new_priv() and ssh2_ed25519_new_priv() accept an OpenSSH sk-ecdsa private key and return an EC key as their context. With OpenSSL 1.1.1/LibreSSL/BoringSSL/AWS-LC that context is an RSA * or DSA *, so the EC_KEY * ends up in RSA_size()/RSA_sign()/RSA_free(); it is reachable via libssh2_userauth_publickey_frommemory()/_fromfile() with an ssh-rsa or ssh-dss public key paired with such a private key (on OpenSSL 3 the call just returns an EVP_PKEY of type EC). The fix adds the same check the sk-ssh-ed25519 branch has; ssh2_pub_privkey() and ssh2_sk_pubkey() still load sk-ecdsa keys, and the new test in test_simple fails without it. I checked open and closed PRs and found none covering this branch.

The sk-ecdsa branch was the only one not matching `want_method`, so
`ssh2_rsa_new_priv()`, `ssh2_dsa_new_priv()` and
`ssh2_ed25519_new_priv()` accepted an OpenSSH sk-ecdsa private key and
returned an EC key as their context. With OpenSSL 1.1.1 and forks the
`EC_KEY` pointer was then used as `RSA`/`DSA`.

Add the same check the sk-ssh-ed25519 branch uses, and a test.
Comment thread tests/test_simple.c Resolved
Comment thread tests/test_simple.c Outdated
"aXN0cDI1NgAAAEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP\n"
"40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QAAAARzc2g6AQAAABCq\n"
"qqqqqqqqqqqqqqqqqqqqAAAAAAAAAAABAgME\n"
"-----END OPENSSH PRIVATE KEY-----\n";

@vszakats vszakats Oct 16, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment documenting the ssh-keygen command used
to generate this key? Or, could we perhaps use an existing key from
the keys/ subdir? If there is no fitting one there, maybe add a new one?

@vszakats vszakats Oct 16, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ssh-keygen -t ecdsa-sk -b  256 -N ''          -m RFC4716 -C 'id_ecdsa_openssh_sk'          -f 'keys/id_ecdsa_openssh_sk'            # empty comment

This one produces some extra stuff at the end of the blob with my test key at least.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing in keys/ fit, so I've added keys/id_ecdsa_openssh_sk (plus .pub and the Makefile.inc entries) using your command in keys-generate.sh, and the test now embeds that key with a comment pointing back to it. The old blob was hand-built with just a 16-byte handle and empty reserved/comment fields, which is why yours had more at the end. I don't have a FIDO token at hand, so this one was made with OpenSSH's regress/misc/sk-dummy provider (SSH_SK_PROVIDER), which means its key handle is a dummy PEM blob rather than an opaque one. Feel free to regenerate it with a real token if you'd prefer. The test still fails without the fix and passes with it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! No intent to generate it with my hw key. Which key do you think would
be better, the shorter one you included in the C file, or the new one with PEM
EC key embedded in it?

That key could be loaded with ssh2_file_to_blob() in test_simple.c. I guess
it'd would also need srcdir_path() from session_fixture.c to locate it on disk.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd go with the ssh-keygen one. It's reproducible from keys-generate.sh, while the short one was hand-assembled with fake handle/reserved fields. The PEM blob in the handle only comes from sk-dummy, but the parser treats the handle as opaque, so for this test that doesn't matter.

Switched the test to read keys/id_ecdsa_openssh_sk via ssh2_file_to_blob() + srcdir_path(), which also gets rid of the embedded string and the overlength pragmas. It still fails without the fix and passes with it.

Add keys/id_ecdsa_openssh_sk with its keys-generate.sh line and embed
it in the test instead of the hand-crafted blob.
Read keys/id_ecdsa_openssh_sk with `ssh2_file_to_blob()` and
`srcdir_path()` instead of embedding it, which also drops the
-Woverlength-strings pragmas.
Comment thread tests/keys/id_ecdsa_openssh_sk Outdated
5sTkcvUW8xeklmYUp4dXJjdXIyMlpFNGowVnE1Rm9Bb0dDQ3FHU000OQpBd0VIb1VRRFFn
QUUzQmRrWGVPdlBHdGY3WjNLS3ovQ1FycXpQL3QvV1ZzeFh2dkMrbDFiWFlLTGY5Q3ZQaz
RlClY0UTRWZStLdXdZVld5TXh2VHArd2JQeVRNYzJObFkxV1E9PQotLS0tLUVORCBFQyBQ
UklWQVRFIEtFWS0tLS0tCgAAAAAAAAATaWRfZWNkc2Ffb3BlbnNzaF9zawECAwQFBg==

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update, it's looking good, but I wonder if there is
somewhere a public, trusted, test key we could use here?

This one is special because we cannot generate it via keys-generate.sh,
and I couldn't positively confirm that using someone's actual key here
is safe to use either. So were at the situation of uploading a blob to the
repo, one that we cannot reproduce or replace with a fresh one.

I looked the openssh code you mentioned, it built a static lib, but could
not make it work to generate an actual key.

If this is unsolvable, I'm fine with merging this without a test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenSSH's own regress suite has one: regress/unittests/sshkey/testdata/ecdsa_sk1, generated by their mktestdata.sh with the sk-dummy provider (ssh-keygen -w "$SK_DUMMY" -t ecdsa-sk -C "ECDSA-SK test key #1" -N "" -f ecdsa_sk1). It has been in openssh-portable unchanged since 2020, so it has a public origin and no one's real credential is involved.

I've swapped keys/id_ecdsa_openssh_sk for that key, and keys-generate.sh now fetches it from a pinned openssh-portable commit instead of calling ssh-keygen, so the script keeps working and the blob can be re-fetched from a known source. The test still fails without the fix and passes with it.

If you'd still rather land this without the test, I'm fine dropping it.

Replace the locally generated sk-dummy key with ecdsa_sk1 from OpenSSH's
regress/unittests/sshkey/testdata, which has a public origin and a
documented generation command, and fetch it in keys-generate.sh from a
pinned openssh-portable commit so it can be reproduced.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants