From aca7613171937334c47377faf0cf2e4a9126c0d4 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Sat, 27 Nov 2021 22:13:23 -0500 Subject: Pre-patch test proving security flaw re: 32bit key hash comparisons --- tests/badhash_key1.ed25519.key | 7 +++++++ tests/badhash_key2.ed25519.key | 7 +++++++ tests/test_pkey.py | 18 +++++++++++++++++- tests/util.py | 19 ++++++++++++++++++- 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/badhash_key1.ed25519.key create mode 100644 tests/badhash_key2.ed25519.key diff --git a/tests/badhash_key1.ed25519.key b/tests/badhash_key1.ed25519.key new file mode 100644 index 00000000..3e33781b --- /dev/null +++ b/tests/badhash_key1.ed25519.key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQyNTUx +OQAAACCULQjdmVfwpbDAFYz4mhKo6aCiAfkbaC+dEdq5eP1R9QAAAIjXZhzv12Yc7wAAAAtzc2gt +ZWQyNTUxOQAAACCULQjdmVfwpbDAFYz4mhKo6aCiAfkbaC+dEdq5eP1R9QAAAEByeJbhZUBL2aJ6 +wP85amzQuqDJRrNyAGMtDBJ43SURxpQtCN2ZV/ClsMAVjPiaEqjpoKIB+RtoL50R2rl4/VH1AAAA +AAECAwQF +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/badhash_key2.ed25519.key b/tests/badhash_key2.ed25519.key new file mode 100644 index 00000000..bf48edac --- /dev/null +++ b/tests/badhash_key2.ed25519.key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQyNTUx +OQAAACACJbNmFu2Bk34HArxhiRYajoIN03Vr0umfNvsc9atE0AAAAIi5+po1ufqaNQAAAAtzc2gt +ZWQyNTUxOQAAACACJbNmFu2Bk34HArxhiRYajoIN03Vr0umfNvsc9atE0AAAAECh/ZzZJDOZGnil +BxJMm+nOhBpc07IVBjU1ii+S8zqFaAIls2YW7YGTfgcCvGGJFhqOgg3TdWvS6Z82+xz1q0TQAAAA +AAECAwQF +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/test_pkey.py b/tests/test_pkey.py index 18c27bbe..caa6d7b3 100644 --- a/tests/test_pkey.py +++ b/tests/test_pkey.py @@ -31,8 +31,9 @@ from paramiko.py3compat import StringIO, byte_chr, b, bytes, PY2 from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateNumbers from mock import patch +import pytest -from .util import _support +from .util import _support, is_low_entropy # from openssh's ssh-keygen @@ -551,6 +552,21 @@ class KeyTest(unittest.TestCase): self.assertTrue(not pub.can_sign()) self.assertEqual(key, pub) + # No point testing on systems that never exhibited the bug originally + @pytest.mark.skipif( + not is_low_entropy(), reason="Not a low-entropy system" + ) + def test_ed25519_32bit_collision(self): + # Re: 2021.10.19 security report email: two different private keys + # which Paramiko compared as equal on low-entropy platforms. + original = Ed25519Key.from_private_key_file( + _support("badhash_key1.ed25519.key") + ) + generated = Ed25519Key.from_private_key_file( + _support("badhash_key2.ed25519.key") + ) + assert original != generated + def test_ed25519_nonbytes_password(self): # https://github.com/paramiko/paramiko/issues/1039 Ed25519Key.from_private_key_file( diff --git a/tests/util.py b/tests/util.py index 9057f516..1355ce8a 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,11 +1,12 @@ from os.path import dirname, realpath, join import os +import struct import sys import unittest import pytest -from paramiko.py3compat import builtins +from paramiko.py3compat import builtins, PY2 from paramiko.ssh_gss import GSS_AUTH_AVAILABLE @@ -127,3 +128,19 @@ def k5shell(args=None): if not args: args = [os.environ.get("SHELL", "bash")] sys.exit(subprocess.call(args)) + + +def is_low_entropy(): + """ + Attempts to detect whether running interpreter is low-entropy. + + Classified as being in 32-bit mode under Python 2, or 32-bit mode and with + the hash seed set to zero under Python 3. + """ + is_32bit = struct.calcsize("P") == 32 / 8 + if PY2: + return is_32bit + else: + # I don't see a way to tell internally if the hash seed was set this + # way, but env should be plenty sufficient, this is only for testing. + return is_32bit and os.environ.get("PYTHONHASHSEED", None) == "0" -- cgit v1.2.1