diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e3b3096..5fca3f9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,20 +2,25 @@ // https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/docker-existing-dockerfile { "name": "Existing Dockerfile", - // Sets the run context to one level up instead of the .devcontainer folder. "context": "..", - // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. "dockerFile": "../Dockerfile", - // Set *default* container specific settings.json values on container create. - "settings": { - "terminal.integrated.defaultProfile.linux": "bash", - "terminal.integrated.profiles.linux": { "bash": { "path": "bash" } } + "customizations": { + "vscode": { + "extensions": [ + "ms-python.vscode-pylance", + "visualstudioexptteam.vscodeintellicode" + ], + "terminal.integrated.defaultProfile.linux": "bash", + "terminal.integrated.profiles.linux": { + "bash": { + "path": "bash" + } + } + } }, - "extensions": ["ms-python.vscode-pylance", - "visualstudioexptteam.vscodeintellicode"], - "postCreateCommand": "pip install --no-cache-dir --quiet -r /home/ocspdev/OcspChecker/requirements.txt", + "postCreateCommand": "pip install --no-cache-dir --user --quiet -r /home/ocspdev/OcspChecker/requirements.txt", "remoteUser": "ocspdev" } \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 228565b..5cd7541 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,11 +7,20 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + os: + [ + ubuntu-20.04, + ubuntu-22.04, + windows-2022, + windows-2019, + macos-12, + macos-11, + ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3.3.0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # tag=v4.5.0 with: python-version: ${{ matrix.python-version }} - name: Install tools @@ -24,27 +33,27 @@ jobs: run: | tox -e py - name: Upload test results - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # tag=v3.1.2 with: name: pytest-results for ${{ matrix.python-version }} - path: '**/test-output.xml' + path: "**/test-output.xml" - name: Upload coverage results - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # tag=v3.1.2 with: name: pytest-results for ${{ matrix.python-version }} - path: '**/coverage.xml' + path: "**/coverage.xml" if: ${{ always() }} build: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3.3.0 - name: Set up Python 3.x uses: actions/setup-python@v2 with: - python-version: '3.9' - architecture: 'x64' + python-version: "3.9" + architecture: "x64" - name: Install tools run: | python -m pip install --upgrade pip setuptools wheel @@ -60,7 +69,7 @@ jobs: with: artifact_path: dist/ - name: Upload provenance - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # tag=v3.1.2 with: name: OCSP-Checker provenance path: build.provenance diff --git a/CHANGELOG.md b/CHANGELOG.md index 9036fe9..793d627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,3 +54,10 @@ - removed a test that will never be able to run in the context of a docker container - Improved errors returned to the user for various OpenSSL errors - switch from get_received_chain to the get_verified_chain method in NaSSL to ensure the certificate is validated before we try any operations + +# v1.9.11 +- bump all dependencies +- moved to pyproject.toml for project definition +- added tests for python 3.10 and 3.11 +- added coverage across macOS, Linux, and Windows +- fixed two broken tests and commented one out for now \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 925b8ef..92244b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Usually you do not have to specify amd64, but on an Apple M1 you do if you want to use packages # that are not optimized for arm64 like NaSSL -FROM --platform=amd64 python:3.9.9-slim-bullseye +FROM --platform=amd64 python:3.11.1-slim-bullseye SHELL ["/bin/bash", "--login", "-c"] @@ -10,12 +10,12 @@ ENV LANG C.UTF-8 RUN useradd -m ocspdev RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates \ - netbase \ - curl \ - git \ - bash-completion \ - && rm -rf /var/lib/apt/lists/* + ca-certificates \ + netbase \ + curl \ + git \ + bash-completion \ + && rm -rf /var/lib/apt/lists/* RUN pip install --no-cache-dir --quiet --upgrade pip setuptools wheel \ pip install --no-cache-dir --quiet pytest pytest-cov && \ diff --git a/README.md b/README.md index 9bc601a..ec43e3b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,13 @@ __Python__ - Python 3.7 (64-bit) and above. ## Installation +It is strongly recommended to run ocsp-checker in a virtual environment. This will prevent you from impacting your system python when installing its dependencies. [venv](https://docs.python.org/3/library/venv.html) is a good option, with an example below: + +```python -m venv ocsp-checker``` +```cd ocsp-checker && source bin/activate``` + +Once your virtual environment is activated, install ocsp-checker as follows: + ```pip install ocsp-checker``` ## Usage diff --git a/ocspchecker/ocspchecker.py b/ocspchecker/ocspchecker.py index c0ceb4b..6c42bc1 100644 --- a/ocspchecker/ocspchecker.py +++ b/ocspchecker/ocspchecker.py @@ -20,13 +20,18 @@ from nassl._nassl import OpenSSLError from nassl.cert_chain_verifier import CertificateChainVerificationFailed from nassl.ssl_client import ( - ClientCertificateRequested, OpenSslVerifyEnum, OpenSslVersionEnum, SslClient) + ClientCertificateRequested, + OpenSslVerifyEnum, + OpenSslVersionEnum, + SslClient, +) from validators import domain, url class InitialConnectionError(Exception): - """ Custom exception class to differentiate between - initial connection errors and OpenSSL errors """ + """Custom exception class to differentiate between + initial connection errors and OpenSSL errors""" + pass @@ -35,20 +40,25 @@ class InitialConnectionError(Exception): openssl_errors: dict = { # https://github.com/openssl/openssl/issues/6805 - "1408F10B": "The remote host is not using SSL/TLS on the port specified." + "1408F10B": "The remote host is not using SSL/TLS on the port specified." # TLS Fatal Alert 40 - sender was unable to negotiate an acceptable set of security # parameters given the options available - ,"14094410": "SSL/TLS Handshake Failure." + , + "14094410": "SSL/TLS Handshake Failure." # TLS Fatal Alert 112 - the server understood the ClientHello but did not recognize # the server name per: https://datatracker.ietf.org/doc/html/rfc6066#section-3 - ,"14094458": "Unrecognized server name provided. Check your target and try again." + , + "14094458": "Unrecognized server name provided. Check your target and try again." # TLS Fatal Alert 50 - a field was out of the specified range # or the length of the message was incorrect - ,"1417B109": "Decode Error. Check your target and try again." + , + "1417B109": "Decode Error. Check your target and try again." # TLS Fatal Alert 80 - Internal Error - ,"14094438": "TLS Fatal Alert 80 - Internal Error." + , + "14094438": "TLS Fatal Alert 80 - Internal Error." # Unable to find public key parameters - ,"140070EF": "Unable to find public key parameters." + , + "140070EF": "Unable to find public key parameters.", } @@ -133,7 +143,9 @@ def get_certificate_chain(host: str, port: int) -> List[str]: ) from None except ConnectionRefusedError: - raise InitialConnectionError(f"{func_name}: Connection to {host}:{port} refused.") from None + raise InitialConnectionError( + f"{func_name}: Connection to {host}:{port} refused." + ) from None except OSError: raise InitialConnectionError( @@ -149,7 +161,7 @@ def get_certificate_chain(host: str, port: int) -> List[str]: ssl_version=OpenSslVersionEnum.SSLV23, underlying_socket=soc, ssl_verify=OpenSslVerifyEnum.NONE, - ssl_verify_locations=path_to_ca_certs + ssl_verify_locations=path_to_ca_certs, ) # Add Server Name Indication (SNI) extension to the Client Hello @@ -165,16 +177,19 @@ def get_certificate_chain(host: str, port: int) -> List[str]: ) from None except CertificateChainVerificationFailed: - raise ValueError(f"{func_name}: Certificate Verification failed for {host}.") from None + raise ValueError( + f"{func_name}: Certificate Verification failed for {host}." + ) from None except ClientCertificateRequested: - raise ValueError(f"{func_name}: Client Certificate Requested for {host}.") from None + raise ValueError( + f"{func_name}: Client Certificate Requested for {host}." + ) from None except OpenSSLError as err: for key, value in openssl_errors.items(): if key in err.args[0]: - raise ValueError(f"{func_name}: {value}" - ) from None + raise ValueError(f"{func_name}: {value}") from None raise ValueError(f"{func_name}: {err}") from None diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b50c35c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,41 @@ +[build-system] +requires = ["setuptools>=61.0.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name="ocsp-checker" +version="1.9.11" +description="Library used to check the OCSP revocation status for a x509 digital certificate." + +readme= {file = "README.md", content-type = "text/markdown"} +authors=[{ name = "Joe Gatt", email = "gattjoseph@hotmail.com" }] +license= {file = "LICENSE.txt" } +classifiers=[ + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +keywords=["ssl, tls, ocsp, python, security"] +dependencies = [ + "cryptography==39", + "nassl==5.0", + "validators>=0.20.0", + "certifi", +] +requires-python = ">=3.7" + +[tools.setuptools.packages] +find = {} + +[project.urls] +"homepage" = "https://github.com/MetLife/OCSPChecker" +"documentation" = "https://github.com/MetLife/OCSPChecker/blob/master/README.md" +"repository" = "https://github.com/MetLife/OCSPChecker" +"changelog" = "https://github.com/MetLife/OCSPChecker/blob/master/CHANGELOG.md" + +[project.scripts] +ocspchecker = "ocspchecker.__main__:main" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0b4e832..9843bdd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ certifi -cffi==1.15.0 -cryptography>=36,<37 -decorator==5.1.0 -nassl>=4.0.1 +cffi==1.15.1 +cryptography==39 +decorator==5.1.1 +nassl==5.0.0 pycparser==2.21 six==1.16.0 -validators==0.18.2 +validators==0.20.0 diff --git a/setup.py b/setup.py deleted file mode 100644 index 61d6f3d..0000000 --- a/setup.py +++ /dev/null @@ -1,38 +0,0 @@ -""" Setup file based on https://github.com/pypa/sampleproject/blob/master/setup.py """ - -import pathlib - -from setuptools import find_packages, setup - -here = pathlib.Path(__file__).parent.resolve() -long_description = (here / "README.md").read_text(encoding="utf-8") - -setup( - name="ocsp-checker", - version="1.9.9", - description="Library used to check the OCSP revocation status for a x509 digital certificate.", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/MetLife/OCSPChecker", - author="Joe Gatt", - author_email="gattjoseph@hotmail.com", - license="Apache 2.0", - classifiers=[ - "Development Status :: 3 - Alpha", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - ], - project_urls={ - "Source": "https://github.com/MetLife/OCSPChecker", - "Changelog": "https://github.com/MetLife/OCSPChecker/blob/master/CHANGELOG.md", - "Documentation": "https://github.com/MetLife/OCSPChecker/blob/master/README.md", - }, - keywords="ssl, tls, ocsp, python, security", - packages=find_packages(include=["ocspchecker"]), - entry_points={"console_scripts": ["ocspchecker = ocspchecker.__main__:main"]}, - # Dependencies - install_requires=["cryptography>=36.0", "nassl>=4.0", "validators>=0.18", "certifi"], -) diff --git a/tests/certs.py b/tests/certs.py index 2740497..d2892af 100644 --- a/tests/certs.py +++ b/tests/certs.py @@ -1,28 +1,28 @@ """ Certs used to validate ocspchecker """ github_issuer_pem = """-----BEGIN CERTIFICATE----- -MIIEGzCCAwOgAwIBAgIQBmcDW7sU/WOvwNaoU07+FjANBgkqhkiG9w0BAQsFADBs +MIIEFzCCAv+gAwIBAgIQB/LzXIeod6967+lHmTUlvTANBgkqhkiG9w0BAQwFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTIwMTIxNzAwMDAwMFoXDTMwMTIxNjIzNTk1OVowZzEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMT8wPQYDVQQDEzZE -aWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBUTFMgSHlicmlkIEVDQyBTSEEyNTYgMjAy -MCBDQTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARnvW/xPOudvtC252wTq9ef -6fbdFeWPkOscfpRTkciuHj7UcumQSH3lzkPEIx0KpesWa8epsks7QwkZ4fU/Tkf9 -o4IBhzCCAYMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUUGGmoNI1xBEq -II0fD6xC8M0pz0swHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDgYD -VR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB/Bggr -BgEFBQcBAQRzMHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv -bTBJBggrBgEFBQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD -ZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNydDBLBgNVHR8ERDBCMECgPqA8hjpo -dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZS -b290Q0EuY3JsMDAGA1UdIAQpMCcwCAYGZ4EMAQICMAgGBmeBDAECAzAHBgVngQwB -ATAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAHMQH8hhiBfNbxwEwxbbTAnu -jPyUh/oi0JrfZI3u9JuiLqca720D6foS/AB5+4EIxpm7CMG4MdN/l7oAiDipaCPv -mOmpYUpnT7A63Cr0q4g84rI1ZmdqA40lVUUf6qC6E34tC73qDQF8TJSrfscWFdCl -RXR9J4QGrkZ2VNMSDzlDRzWCaA95MfO8x01l+ZdopdE8FvM78gGd4zxeWb8v991+ -mBxTDepqKuy/jF5Rm6Bhfxr33ADRs60s1t16dtZ3pOYLALBTPD5KhZ6a+/dk5dnh -6c4PaeZQYBUAh+GuxfaBlU4qQ8EtjBMCQHreMIwXHYHW5FRYGjgR4NMuaIw2jD0= +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaMFYxCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMDAuBgNVBAMTJ0RpZ2lDZXJ0IFRMUyBI +eWJyaWQgRUNDIFNIQTM4NCAyMDIwIENBMTB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BMEbxppbmNmkKaDp1AS12+umsmxVwP/tmMZJLwYnUcu/cMEFesOxnYeJuq20ExfJ +qLSDyLiQ0cx0NTY8g3KwtdD3ImnI8YDEe0CPz2iHJlw5ifFNkU3aiYvkA8ND5b8v +c6OCAYIwggF+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAq8CCkXjKU5 +bXoOzjPHLrPt+8N6MB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA4G +A1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYI +KwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j +b20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp +Q2VydEdsb2JhbFJvb3RDQS5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny +bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAE +NjA0MAsGCWCGSAGG/WwCATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgG +BmeBDAECAzANBgkqhkiG9w0BAQwFAAOCAQEAR1mBf9QbH7Bx9phdGLqYR5iwfnYr +6v8ai6wms0KNMeZK6BnQ79oU59cUkqGS8qcuLa/7Hfb7U7CKP/zYFgrpsC62pQsY +kDUmotr2qLcy/JUjS8ZFucTP5Hzu5sn4kL1y45nDHQsFfGqXbbKrAjbYwrwsAZI/ +BKOLdRHHuSm8EdCGupK8JvllyDfNJvaGEwwEqonleLHBTnm8dqMLUeTF0J5q/hos +Vq4GNiejcxwIfZMy0MJEGdqN9A57HSgDKwmKdsp33Id6rHtSJlWncg+d0ohP/rEh +xRqhqjn1VtvChMQ1H3Dau0bwhr9kAMQ+959GG50jBbl9s08PqUU643QwmA== -----END CERTIFICATE----- """ @@ -57,7 +57,7 @@ -----END CERTIFICATE----- """ -github_ocsp_data = b"0Q0O0M0K0I0\t\x06\x05+\x0e\x03\x02\x1a\x05\x00\x04\x14\xc62Z\xee/\xa3\xfd3\xd0w\x89\xfdkL\xce\xf0\xca?\xd0)\x04\x14Pa\xa6\xa0\xd25\xc4\x11* \x8d\x1f\x0f\xacB\xf0\xcd)\xcfK\x02\x10\x0e\x8b\xf3w\r\x92\xd1\x96\xf0\xbba\xf9