From 889d05a8e40056b1908c13fd1b9a1b17e28aae4c Mon Sep 17 00:00:00 2001 From: Jeff West Date: Fri, 22 May 2026 06:59:12 -0500 Subject: [PATCH 1/4] polish(auth): targeted error for OpenSSH-format private keys ssh-keygen's default output since OpenSSH 7.8 is the -----BEGIN OPENSSH PRIVATE KEY----- format, which cryptography's load_pem_private_key cannot parse. Users who generated their Kalshi key with 'ssh-keygen -t rsa' previously saw the generic 'Invalid PEM private key ... Ensure the key is ... PKCS8 PEM format' message and went hunting in the wrong direction. Detect the OpenSSH magic header before calling load_pem_private_key and raise a targeted KalshiAuthError naming the format and giving the exact conversion commands (ssh-keygen -p -m PKCS8 -f -N '', or openssl genpkey). The existing PKCS8/PKCS1 path and passphrase handling are unchanged; genuinely malformed PEM still surfaces the original message. Closes #335 --- kalshi/auth.py | 8 ++++++++ tests/test_auth.py | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/kalshi/auth.py b/kalshi/auth.py index 9f09741d..f9cd95c6 100644 --- a/kalshi/auth.py +++ b/kalshi/auth.py @@ -123,6 +123,14 @@ def from_pem( if password is not None: raw = password() if callable(password) else password pw_bytes = raw.encode("utf-8") if isinstance(raw, str) else raw + if pem_data.lstrip().startswith(b"-----BEGIN OPENSSH PRIVATE KEY-----"): + raise KalshiAuthError( + "OpenSSH private-key format detected (-----BEGIN OPENSSH PRIVATE KEY-----); " + "Kalshi requires PKCS8 PEM. Convert in place with: " + "ssh-keygen -p -m PKCS8 -f -N '' " + "or generate a fresh PKCS8 key with: " + "openssl genpkey -algorithm RSA -out -pkeyopt rsa_keygen_bits:2048" + ) try: private_key = serialization.load_pem_private_key(pem_data, password=pw_bytes) except TypeError as e: diff --git a/tests/test_auth.py b/tests/test_auth.py index 520acf32..aac4804b 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -512,6 +512,16 @@ def test_rejects_non_rsa_key(self) -> None: with pytest.raises(KalshiAuthError, match="Expected RSA"): KalshiAuth.from_pem("key-4", ec_pem) + def test_issue_335_from_pem_openssh_format_error_message(self) -> None: + openssh_pem = ( + b"-----BEGIN OPENSSH PRIVATE KEY-----\n" + b"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAAB\n" + b"-----END OPENSSH PRIVATE KEY-----\n" + ) + with pytest.raises(KalshiAuthError, match="OpenSSH private-key format") as exc: + KalshiAuth.from_pem("key-ossh", openssh_pem) + assert "ssh-keygen -p -m PKCS8" in str(exc.value) + class TestFromEnv: def test_missing_key_id(self, monkeypatch: pytest.MonkeyPatch) -> None: From af06a0fb0afbe4a478403c913c041f000c70cea9 Mon Sep 17 00:00:00 2001 From: Jeff West Date: Fri, 22 May 2026 07:08:09 -0500 Subject: [PATCH 2/4] polish(auth): drop -N '' from OpenSSH conversion hint to avoid silent passphrase strip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The round-1 hint suggested 'ssh-keygen -p -m PKCS8 -f -N \'\'', where -N '' sets an empty new passphrase. For users whose source key is passphrase-protected, this silently strips the passphrase — a security surprise. Drop -N '' so ssh-keygen prompts interactively for both the current and the new passphrase, which is safe for encrypted and unencrypted source keys alike. Annotate the prompt behaviour in the hint so the interactive step is not unexpected. Test updated to assert the new hint substring and that -N '' is absent. --- kalshi/auth.py | 3 ++- tests/test_auth.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/kalshi/auth.py b/kalshi/auth.py index f9cd95c6..04a94b09 100644 --- a/kalshi/auth.py +++ b/kalshi/auth.py @@ -127,7 +127,8 @@ def from_pem( raise KalshiAuthError( "OpenSSH private-key format detected (-----BEGIN OPENSSH PRIVATE KEY-----); " "Kalshi requires PKCS8 PEM. Convert in place with: " - "ssh-keygen -p -m PKCS8 -f -N '' " + "ssh-keygen -p -m PKCS8 -f " + "(you will be prompted for the current passphrase, if any, and a new one), " "or generate a fresh PKCS8 key with: " "openssl genpkey -algorithm RSA -out -pkeyopt rsa_keygen_bits:2048" ) diff --git a/tests/test_auth.py b/tests/test_auth.py index aac4804b..35fabbe0 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -520,7 +520,9 @@ def test_issue_335_from_pem_openssh_format_error_message(self) -> None: ) with pytest.raises(KalshiAuthError, match="OpenSSH private-key format") as exc: KalshiAuth.from_pem("key-ossh", openssh_pem) - assert "ssh-keygen -p -m PKCS8" in str(exc.value) + msg = str(exc.value) + assert "ssh-keygen -p -m PKCS8 -f " in msg + assert "-N ''" not in msg class TestFromEnv: From 07ef4908ef155d66593b398815ea8fe5dde1c927 Mon Sep 17 00:00:00 2001 From: Jeff West Date: Fri, 22 May 2026 07:15:51 -0500 Subject: [PATCH 3/4] polish(auth): hoist OpenSSH check before password handling; document test negative assertion --- kalshi/auth.py | 8 ++++---- tests/test_auth.py | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/kalshi/auth.py b/kalshi/auth.py index 04a94b09..f5923b85 100644 --- a/kalshi/auth.py +++ b/kalshi/auth.py @@ -119,10 +119,6 @@ def from_pem( """ if isinstance(pem_data, str): pem_data = pem_data.encode("utf-8") - pw_bytes: bytes | None = None - if password is not None: - raw = password() if callable(password) else password - pw_bytes = raw.encode("utf-8") if isinstance(raw, str) else raw if pem_data.lstrip().startswith(b"-----BEGIN OPENSSH PRIVATE KEY-----"): raise KalshiAuthError( "OpenSSH private-key format detected (-----BEGIN OPENSSH PRIVATE KEY-----); " @@ -132,6 +128,10 @@ def from_pem( "or generate a fresh PKCS8 key with: " "openssl genpkey -algorithm RSA -out -pkeyopt rsa_keygen_bits:2048" ) + pw_bytes: bytes | None = None + if password is not None: + raw = password() if callable(password) else password + pw_bytes = raw.encode("utf-8") if isinstance(raw, str) else raw try: private_key = serialization.load_pem_private_key(pem_data, password=pw_bytes) except TypeError as e: diff --git a/tests/test_auth.py b/tests/test_auth.py index 35fabbe0..53495a43 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -522,6 +522,8 @@ def test_issue_335_from_pem_openssh_format_error_message(self) -> None: KalshiAuth.from_pem("key-ossh", openssh_pem) msg = str(exc.value) assert "ssh-keygen -p -m PKCS8 -f " in msg + # regression: dropped from earlier draft (would silently strip passphrase + # on encrypted OpenSSH keys) assert "-N ''" not in msg From 0b93ccaafdc225384ea7d33028fa081f10a11dc6 Mon Sep 17 00:00:00 2001 From: Jeff West Date: Fri, 22 May 2026 07:22:53 -0500 Subject: [PATCH 4/4] polish(tests): restate -N '' assertion comment as invariant, not PR history --- tests/test_auth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_auth.py b/tests/test_auth.py index 53495a43..01902f4e 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -522,8 +522,8 @@ def test_issue_335_from_pem_openssh_format_error_message(self) -> None: KalshiAuth.from_pem("key-ossh", openssh_pem) msg = str(exc.value) assert "ssh-keygen -p -m PKCS8 -f " in msg - # regression: dropped from earlier draft (would silently strip passphrase - # on encrypted OpenSSH keys) + # -N '' would silently strip the passphrase on encrypted OpenSSH keys — + # must not appear in the message assert "-N ''" not in msg