Spaces:
Configuration error
Configuration error
Upload folder using huggingface_hub (part 2)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/INSTALLER +1 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/METADATA +110 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/RECORD +199 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/WHEEL +5 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/licenses/LICENSE +3 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/licenses/LICENSE.APACHE +202 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/licenses/LICENSE.BSD +27 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json +1365 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/sboms/sbom.json +43 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hashes.py +357 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hmac.py +13 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hpke.py +27 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/__init__.py +32 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/argon2.py +17 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py +16 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py +16 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py +26 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py +13 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py +19 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py +13 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/keywrap.py +25 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/padding.py +69 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/poly1305.py +11 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/__init__.py +85 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/base.py +16 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py +176 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py +412 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/ssh.py +1615 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py +9 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py +101 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/totp.py +56 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/py.typed +0 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/utils.py +136 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/x509/__init__.py +270 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/x509/base.py +834 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/x509/certificate_transparency.py +35 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/x509/extensions.py +2533 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/x509/general_name.py +281 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/x509/name.py +489 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/x509/ocsp.py +379 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/x509/oid.py +37 -0
- .mcp-venv/lib/python3.13/site-packages/cryptography/x509/verification.py +34 -0
- .mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/INSTALLER +1 -0
- .mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/METADATA +202 -0
- .mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/RECORD +29 -0
- .mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/WHEEL +5 -0
- .mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt +22 -0
- .mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/top_level.txt +1 -0
- .mcp-venv/lib/python3.13/site-packages/h11/__init__.py +62 -0
- .mcp-venv/lib/python3.13/site-packages/h11/_abnf.py +132 -0
.mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
.mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: cryptography
|
| 3 |
+
Version: 50.0.0
|
| 4 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 5 |
+
Classifier: Intended Audience :: Developers
|
| 6 |
+
Classifier: Natural Language :: English
|
| 7 |
+
Classifier: Operating System :: MacOS :: MacOS X
|
| 8 |
+
Classifier: Operating System :: POSIX
|
| 9 |
+
Classifier: Operating System :: POSIX :: BSD
|
| 10 |
+
Classifier: Operating System :: POSIX :: Linux
|
| 11 |
+
Classifier: Operating System :: Microsoft :: Windows
|
| 12 |
+
Classifier: Programming Language :: Python
|
| 13 |
+
Classifier: Programming Language :: Python :: 3
|
| 14 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 15 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.14
|
| 21 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 22 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 23 |
+
Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable
|
| 24 |
+
Classifier: Topic :: Security :: Cryptography
|
| 25 |
+
Requires-Dist: cffi>=2.0.0 ; platform_python_implementation != 'PyPy'
|
| 26 |
+
Requires-Dist: typing-extensions>=4.13.2 ; python_full_version < '3.11'
|
| 27 |
+
Requires-Dist: bcrypt>=3.1.5 ; extra == 'ssh'
|
| 28 |
+
Provides-Extra: ssh
|
| 29 |
+
License-File: LICENSE
|
| 30 |
+
License-File: LICENSE.APACHE
|
| 31 |
+
License-File: LICENSE.BSD
|
| 32 |
+
Summary: cryptography is a package which provides cryptographic recipes and primitives to Python developers.
|
| 33 |
+
Author-email: The Python Cryptographic Authority and individual contributors <cryptography-dev@python.org>
|
| 34 |
+
License-Expression: Apache-2.0 OR BSD-3-Clause
|
| 35 |
+
Requires-Python: >=3.9, !=3.9.0, !=3.9.1
|
| 36 |
+
Description-Content-Type: text/x-rst; charset=UTF-8
|
| 37 |
+
Project-URL: changelog, https://cryptography.io/en/latest/changelog/
|
| 38 |
+
Project-URL: documentation, https://cryptography.io/
|
| 39 |
+
Project-URL: homepage, https://github.com/pyca/cryptography
|
| 40 |
+
Project-URL: issues, https://github.com/pyca/cryptography/issues
|
| 41 |
+
Project-URL: source, https://github.com/pyca/cryptography/
|
| 42 |
+
|
| 43 |
+
pyca/cryptography
|
| 44 |
+
=================
|
| 45 |
+
|
| 46 |
+
.. image:: https://img.shields.io/pypi/v/cryptography.svg
|
| 47 |
+
:target: https://pypi.org/project/cryptography/
|
| 48 |
+
:alt: Latest Version
|
| 49 |
+
|
| 50 |
+
.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest
|
| 51 |
+
:target: https://cryptography.io
|
| 52 |
+
:alt: Latest Docs
|
| 53 |
+
|
| 54 |
+
.. image:: https://github.com/pyca/cryptography/actions/workflows/ci.yml/badge.svg
|
| 55 |
+
:target: https://github.com/pyca/cryptography/actions/workflows/ci.yml?query=branch%3Amain
|
| 56 |
+
|
| 57 |
+
``cryptography`` is a package which provides cryptographic recipes and
|
| 58 |
+
primitives to Python developers. Our goal is for it to be your "cryptographic
|
| 59 |
+
standard library". It supports Python 3.9+ and PyPy3 7.3.11+.
|
| 60 |
+
|
| 61 |
+
``cryptography`` includes both high level recipes and low level interfaces to
|
| 62 |
+
common cryptographic algorithms such as symmetric ciphers, message digests, and
|
| 63 |
+
key derivation functions. For example, to encrypt something with
|
| 64 |
+
``cryptography``'s high level symmetric encryption recipe:
|
| 65 |
+
|
| 66 |
+
.. code-block:: pycon
|
| 67 |
+
|
| 68 |
+
>>> from cryptography.fernet import Fernet
|
| 69 |
+
>>> # Put this somewhere safe!
|
| 70 |
+
>>> key = Fernet.generate_key()
|
| 71 |
+
>>> f = Fernet(key)
|
| 72 |
+
>>> token = f.encrypt(b"A really secret message. Not for prying eyes.")
|
| 73 |
+
>>> token
|
| 74 |
+
b'...'
|
| 75 |
+
>>> f.decrypt(token)
|
| 76 |
+
b'A really secret message. Not for prying eyes.'
|
| 77 |
+
|
| 78 |
+
You can find more information in the `documentation`_.
|
| 79 |
+
|
| 80 |
+
You can install ``cryptography`` with:
|
| 81 |
+
|
| 82 |
+
.. code-block:: console
|
| 83 |
+
|
| 84 |
+
$ pip install cryptography
|
| 85 |
+
|
| 86 |
+
For full details see `the installation documentation`_.
|
| 87 |
+
|
| 88 |
+
Discussion
|
| 89 |
+
~~~~~~~~~~
|
| 90 |
+
|
| 91 |
+
If you run into bugs, you can file them in our `issue tracker`_.
|
| 92 |
+
|
| 93 |
+
We maintain a `cryptography-dev`_ mailing list for development discussion.
|
| 94 |
+
|
| 95 |
+
You can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get
|
| 96 |
+
involved.
|
| 97 |
+
|
| 98 |
+
Security
|
| 99 |
+
~~~~~~~~
|
| 100 |
+
|
| 101 |
+
Need to report a security issue? Please consult our `security reporting`_
|
| 102 |
+
documentation.
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
.. _`documentation`: https://cryptography.io/
|
| 106 |
+
.. _`the installation documentation`: https://cryptography.io/en/latest/installation/
|
| 107 |
+
.. _`issue tracker`: https://github.com/pyca/cryptography/issues
|
| 108 |
+
.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev
|
| 109 |
+
.. _`security reporting`: https://cryptography.io/en/latest/security/
|
| 110 |
+
|
.mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/RECORD
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cryptography-50.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 2 |
+
cryptography-50.0.0.dist-info/METADATA,sha256=1ONF-Cy3g4d9cyYfADA1d83yDRMikXbC0DoKVgPLOrU,4343
|
| 3 |
+
cryptography-50.0.0.dist-info/RECORD,,
|
| 4 |
+
cryptography-50.0.0.dist-info/WHEEL,sha256=ObQlzmyJjoP4uM3utl2uaQ06ufI4LKnDhg53v200B3o,109
|
| 5 |
+
cryptography-50.0.0.dist-info/licenses/LICENSE,sha256=Pgx8CRqUi4JTO6mP18u0BDLW8amsv4X1ki0vmak65rs,197
|
| 6 |
+
cryptography-50.0.0.dist-info/licenses/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360
|
| 7 |
+
cryptography-50.0.0.dist-info/licenses/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532
|
| 8 |
+
cryptography-50.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json,sha256=37hpPvBk1cczEk7Pe2369H-MfrP0RYEqmbiTbjdGtcA,45685
|
| 9 |
+
cryptography-50.0.0.dist-info/sboms/sbom.json,sha256=Ow5KjdWCtUUDLAwciwzmVqJAdsCoTtvzWfopBsB4OOU,1206
|
| 10 |
+
cryptography/__about__.py,sha256=rM2noC1TeiQiQ4o8wL-OmtXas5OXwhkWPFo7yTZ-XSM,445
|
| 11 |
+
cryptography/__init__.py,sha256=mthuUrTd4FROCpUYrTIqhjz6s6T9djAZrV7nZ1oMm2o,364
|
| 12 |
+
cryptography/__pycache__/__about__.cpython-313.pyc,,
|
| 13 |
+
cryptography/__pycache__/__init__.cpython-313.pyc,,
|
| 14 |
+
cryptography/__pycache__/cobblestone.cpython-313.pyc,,
|
| 15 |
+
cryptography/__pycache__/exceptions.cpython-313.pyc,,
|
| 16 |
+
cryptography/__pycache__/fernet.cpython-313.pyc,,
|
| 17 |
+
cryptography/__pycache__/utils.cpython-313.pyc,,
|
| 18 |
+
cryptography/cobblestone.py,sha256=T_-F08-AUf3ljaypZeklNTV1hWObH57oo45mVSzk9wc,693
|
| 19 |
+
cryptography/exceptions.py,sha256=835EWILc2fwxw-gyFMriciC2SqhViETB10LBSytnDIc,1087
|
| 20 |
+
cryptography/fernet.py,sha256=2sLCPOmejhOVbudcS3QsM1PIuHbxCeRcRtWkncQ3mCo,7687
|
| 21 |
+
cryptography/hazmat/__init__.py,sha256=5IwrLWrVp0AjEr_4FdWG_V057NSJGY_W4egNNsuct0g,455
|
| 22 |
+
cryptography/hazmat/__pycache__/__init__.cpython-313.pyc,,
|
| 23 |
+
cryptography/hazmat/__pycache__/_oid.cpython-313.pyc,,
|
| 24 |
+
cryptography/hazmat/_oid.py,sha256=Ohe2nUE6XjKx1UF6TAsGUy2v8GUJ-Vi9DS4yxSukt5U,18150
|
| 25 |
+
cryptography/hazmat/asn1/__init__.py,sha256=YqYToHGFjLOKigRC00HGj6hrjEwbu1SPpAiu6-u-fao,812
|
| 26 |
+
cryptography/hazmat/asn1/__pycache__/__init__.cpython-313.pyc,,
|
| 27 |
+
cryptography/hazmat/asn1/__pycache__/asn1.cpython-313.pyc,,
|
| 28 |
+
cryptography/hazmat/asn1/asn1.py,sha256=9mcfrmdvK7KtjhRYofMzAjW0u7nvqbfsvPfbhNQK1BQ,19152
|
| 29 |
+
cryptography/hazmat/backends/__init__.py,sha256=O5jvKFQdZnXhKeqJ-HtulaEL9Ni7mr1mDzZY5kHlYhI,361
|
| 30 |
+
cryptography/hazmat/backends/__pycache__/__init__.cpython-313.pyc,,
|
| 31 |
+
cryptography/hazmat/backends/openssl/__init__.py,sha256=p3jmJfnCag9iE5sdMrN6VvVEu55u46xaS_IjoI0SrmA,305
|
| 32 |
+
cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-313.pyc,,
|
| 33 |
+
cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-313.pyc,,
|
| 34 |
+
cryptography/hazmat/backends/openssl/backend.py,sha256=0NczFbiwGxfq9qi28oHYr08r_r7VQg4AXHjb0oFPwqA,10475
|
| 35 |
+
cryptography/hazmat/bindings/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
|
| 36 |
+
cryptography/hazmat/bindings/__pycache__/__init__.cpython-313.pyc,,
|
| 37 |
+
cryptography/hazmat/bindings/_rust.abi3.so,sha256=YT7FcFlL4mkc4Y_oqlGqRxZYTA5Ybsl915DEuXV7WoM,14357504
|
| 38 |
+
cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=uUnnvOq_uameYnBNpmsv3SRAVO0WQGpjI58aR1lL4Kw,2285
|
| 39 |
+
cryptography/hazmat/bindings/_rust/_openssl.pyi,sha256=T_5TpYO8bghIPjAQ3BtAABqw-CcYskHp92wm5PNY6dc,228
|
| 40 |
+
cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=BrGjC8J6nwuS-r3EVcdXJB8ndotfY9mbQYOfpbPG0HA,354
|
| 41 |
+
cryptography/hazmat/bindings/_rust/cobblestone.pyi,sha256=9lUf1W1hTrszEJ4YpScJNIPSD9BOM7o-C82-mtSDG9o,1325
|
| 42 |
+
cryptography/hazmat/bindings/_rust/declarative_asn1.pyi,sha256=MBmOZzLTdmPJ6-1K6Aq3PTuHJ1MGgRfIu0hIgag7ocE,3866
|
| 43 |
+
cryptography/hazmat/bindings/_rust/exceptions.pyi,sha256=exXr2xw_0pB1kk93cYbM3MohbzoUkjOms1ZMUi0uQZE,640
|
| 44 |
+
cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=VPVWuKHI9EMs09ZLRYAGvR0Iz0mCMmEzXAkgJHovpoM,4020
|
| 45 |
+
cryptography/hazmat/bindings/_rust/openssl/__init__.pyi,sha256=O6GOyvDxt6ylicB2aFGuu5RcGZy9UjKnYqGR8rqzvdw,1578
|
| 46 |
+
cryptography/hazmat/bindings/_rust/openssl/aead.pyi,sha256=7d--xdc0vuzRe4S2uuQaGm-9GGKv4NcSLQcLyWcaD6s,4582
|
| 47 |
+
cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi,sha256=LhPzHWSXJq4grAJXn6zSvSSdV-aYIIscHDwIPlJGGPs,1315
|
| 48 |
+
cryptography/hazmat/bindings/_rust/openssl/cmac.pyi,sha256=nPH0X57RYpsAkRowVpjQiHE566ThUTx7YXrsadmrmHk,564
|
| 49 |
+
cryptography/hazmat/bindings/_rust/openssl/dh.pyi,sha256=Z3TC-G04-THtSdAOPLM1h2G7ml5bda1ElZUcn5wpuhk,1564
|
| 50 |
+
cryptography/hazmat/bindings/_rust/openssl/dsa.pyi,sha256=qBtkgj2albt2qFcnZ9UDrhzoNhCVO7HTby5VSf1EXMI,1299
|
| 51 |
+
cryptography/hazmat/bindings/_rust/openssl/ec.pyi,sha256=zJy0pRa5n-_p2dm45PxECB_-B6SVZyNKfjxFDpPqT38,1691
|
| 52 |
+
cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi,sha256=VXfXd5G6hUivg399R1DYdmW3eTb0EebzDTqjRC2gaRw,532
|
| 53 |
+
cryptography/hazmat/bindings/_rust/openssl/ed448.pyi,sha256=Yx49lqdnjsD7bxiDV1kcaMrDktug5evi5a6zerMiy2s,514
|
| 54 |
+
cryptography/hazmat/bindings/_rust/openssl/hashes.pyi,sha256=bFe3T13UeyRInUJMD-R5ArGTGLqWDiWo2Lv2KGzUbf4,1076
|
| 55 |
+
cryptography/hazmat/bindings/_rust/openssl/hmac.pyi,sha256=BXZn7NDjL3JAbYW0SQ8pg1iyC5DbQXVhUAiwsi8DFR8,702
|
| 56 |
+
cryptography/hazmat/bindings/_rust/openssl/hpke.pyi,sha256=p_hGLn6YH-EarJF1V5bUsUtjipviEApcR5O0sUHJLtc,2914
|
| 57 |
+
cryptography/hazmat/bindings/_rust/openssl/kdf.pyi,sha256=HiiLdEB9nqMsTvMwiFppQpfPz6fHUz9_gpjEB_XAOXA,6619
|
| 58 |
+
cryptography/hazmat/bindings/_rust/openssl/keys.pyi,sha256=teIt8M6ZEMJrn4s3W0UnW0DZ-30Jd68WnSsKKG124l0,912
|
| 59 |
+
cryptography/hazmat/bindings/_rust/openssl/keywrap.pyi,sha256=mGCJEoQ4lxi4CROTBEuUzb9U4ky8n52L3iDvCOXNBRI,733
|
| 60 |
+
cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi,sha256=H84XDW0TQ3LHhdGJD5uPaDT5ijEiK6MZytXYc8rBNf4,1421
|
| 61 |
+
cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi,sha256=byoeus4-lIOlWlEiRQdCc00qCkXpOB5peBY6yo9ik8s,835
|
| 62 |
+
cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi,sha256=_SW9NtQ5FDlAbdclFtWpT4lGmxKIKHpN-4j8J2BzYfQ,585
|
| 63 |
+
cryptography/hazmat/bindings/_rust/openssl/rsa.pyi,sha256=2OQCNSXkxgc-3uw1xiCCloIQTV6p9_kK79Yu0rhZgPc,1364
|
| 64 |
+
cryptography/hazmat/bindings/_rust/openssl/x25519.pyi,sha256=ewn4GpQyb7zPwE-ni7GtyQgMC0A1mLuqYsSyqv6nI_s,523
|
| 65 |
+
cryptography/hazmat/bindings/_rust/openssl/x448.pyi,sha256=juTZTmli8jO_5Vcufg-vHvx_tCyezmSLIh_9PU3TczI,505
|
| 66 |
+
cryptography/hazmat/bindings/_rust/pkcs12.pyi,sha256=vEEd5wDiZvb8ZGFaziLCaWLzAwoG_tvPUxLQw5_uOl8,1605
|
| 67 |
+
cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=txGBJijqZshEcqra6byPNbnisIdlxzOSIHP2hl9arPs,1601
|
| 68 |
+
cryptography/hazmat/bindings/_rust/test_support.pyi,sha256=PPhld-WkO743iXFPebeG0LtgK0aTzGdjcIsay1Gm5GE,757
|
| 69 |
+
cryptography/hazmat/bindings/_rust/x509.pyi,sha256=LVT4htBNse5gijQh4GhVNYEbAO0E1ANgE28tWqw3g4I,10211
|
| 70 |
+
cryptography/hazmat/bindings/openssl/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
|
| 71 |
+
cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-313.pyc,,
|
| 72 |
+
cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-313.pyc,,
|
| 73 |
+
cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-313.pyc,,
|
| 74 |
+
cryptography/hazmat/bindings/openssl/_conditional.py,sha256=vfzi-xHdStAcJQjLqqOlSos94L8D_tqFtBe7Ovw-KtY,5611
|
| 75 |
+
cryptography/hazmat/bindings/openssl/binding.py,sha256=FsuLxbTnrqW60-dAWtakgufeq7TnqaQai1Pc7Dowrjo,3706
|
| 76 |
+
cryptography/hazmat/decrepit/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
|
| 77 |
+
cryptography/hazmat/decrepit/__pycache__/__init__.cpython-313.pyc,,
|
| 78 |
+
cryptography/hazmat/decrepit/ciphers/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
|
| 79 |
+
cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-313.pyc,,
|
| 80 |
+
cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-313.pyc,,
|
| 81 |
+
cryptography/hazmat/decrepit/ciphers/__pycache__/modes.cpython-313.pyc,,
|
| 82 |
+
cryptography/hazmat/decrepit/ciphers/algorithms.py,sha256=WSPjM2sXfrLDXrtN5R7N6rA1XF7b93I1e_lvD4yx2oA,3752
|
| 83 |
+
cryptography/hazmat/decrepit/ciphers/modes.py,sha256=Oq_PEwCke5OLczOfr_vzAOJ6wPx-rlsvHAXPBuh5b9o,1649
|
| 84 |
+
cryptography/hazmat/primitives/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
|
| 85 |
+
cryptography/hazmat/primitives/__pycache__/__init__.cpython-313.pyc,,
|
| 86 |
+
cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-313.pyc,,
|
| 87 |
+
cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-313.pyc,,
|
| 88 |
+
cryptography/hazmat/primitives/__pycache__/_modes.cpython-313.pyc,,
|
| 89 |
+
cryptography/hazmat/primitives/__pycache__/_serialization.cpython-313.pyc,,
|
| 90 |
+
cryptography/hazmat/primitives/__pycache__/cmac.cpython-313.pyc,,
|
| 91 |
+
cryptography/hazmat/primitives/__pycache__/constant_time.cpython-313.pyc,,
|
| 92 |
+
cryptography/hazmat/primitives/__pycache__/hashes.cpython-313.pyc,,
|
| 93 |
+
cryptography/hazmat/primitives/__pycache__/hmac.cpython-313.pyc,,
|
| 94 |
+
cryptography/hazmat/primitives/__pycache__/hpke.cpython-313.pyc,,
|
| 95 |
+
cryptography/hazmat/primitives/__pycache__/keywrap.cpython-313.pyc,,
|
| 96 |
+
cryptography/hazmat/primitives/__pycache__/padding.cpython-313.pyc,,
|
| 97 |
+
cryptography/hazmat/primitives/__pycache__/poly1305.cpython-313.pyc,,
|
| 98 |
+
cryptography/hazmat/primitives/_asymmetric.py,sha256=RhgcouUB6HTiFDBrR1LxqkMjpUxIiNvQ1r_zJjRG6qQ,532
|
| 99 |
+
cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=Eh3i7lwedHfi0eLSsH93PZxQKzY9I6lkK67vL4V5tOc,1522
|
| 100 |
+
cryptography/hazmat/primitives/_modes.py,sha256=_EiAOD8Jb6WpFXluwkcUT3ECz_TH8xjGvbQlGkNm59Q,3075
|
| 101 |
+
cryptography/hazmat/primitives/_serialization.py,sha256=hi0xJblBAZ8pmZx2lWa-lruphxvvvG3g9DNK0G8Odfs,4440
|
| 102 |
+
cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
|
| 103 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-313.pyc,,
|
| 104 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-313.pyc,,
|
| 105 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-313.pyc,,
|
| 106 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-313.pyc,,
|
| 107 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-313.pyc,,
|
| 108 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-313.pyc,,
|
| 109 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/mldsa.cpython-313.pyc,,
|
| 110 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/mlkem.cpython-313.pyc,,
|
| 111 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-313.pyc,,
|
| 112 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-313.pyc,,
|
| 113 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-313.pyc,,
|
| 114 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-313.pyc,,
|
| 115 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-313.pyc,,
|
| 116 |
+
cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-313.pyc,,
|
| 117 |
+
cryptography/hazmat/primitives/asymmetric/dh.py,sha256=Rnt2fLaDpQEjQSX8fNtlmTXOJHjlt_DKQZ1Vlfhdja4,5793
|
| 118 |
+
cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=kQzTViqAI9PiELNOBco_n6MfP3N9ehIPkSduOpupt0M,4482
|
| 119 |
+
cryptography/hazmat/primitives/asymmetric/ec.py,sha256=LklVCLLxbbQzqiE6HRaYTRauLMphYUw3tE6c8oBIxYQ,10183
|
| 120 |
+
cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=mbffLvs-3PgCDeg1rQXVXh7GLntlVL4ATjkIEc4pt5M,2998
|
| 121 |
+
cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=n0dK6y-VLxuw-zvrDxuPT4598hymgX-1Ry_jq9aUQhE,4002
|
| 122 |
+
cryptography/hazmat/primitives/asymmetric/mldsa.py,sha256=LHMaaU0m66EsSrSodWf4e2kdmdDRTEGRC1y-fCKxGK0,13803
|
| 123 |
+
cryptography/hazmat/primitives/asymmetric/mlkem.py,sha256=wvVkeFT-4of-VcUAER2-zI_5hXT6rwTJPMCQ8gF9GTA,7901
|
| 124 |
+
cryptography/hazmat/primitives/asymmetric/padding.py,sha256=jbgajvGHYwkPjMDljHu5lxMBlDfmCxWUe2AF2S6KE10,3678
|
| 125 |
+
cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=lIE7lGe449W5URLTKCpvFGerITouKoZV9GwuTzScsSo,8492
|
| 126 |
+
cryptography/hazmat/primitives/asymmetric/types.py,sha256=SjJhTrnFRLlTPdYrBD1poMi6cjYG-fh0fSIaQn3QUiw,2588
|
| 127 |
+
cryptography/hazmat/primitives/asymmetric/utils.py,sha256=Qs8Re9GFPjW_tNp_73IeJEjPCf0slOIsWOxo6qymT6k,821
|
| 128 |
+
cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=lQcgUk-Piubj8ynFNWQeYme3tYZMgHQqkRKkIDOHLzo,3888
|
| 129 |
+
cryptography/hazmat/primitives/asymmetric/x448.py,sha256=b37ig7k7poG6SJDry42j-ElCj5q4eQ0X5CFmN0Sn8z8,3913
|
| 130 |
+
cryptography/hazmat/primitives/ciphers/__init__.py,sha256=eyEXmjk6_CZXaOPYDr7vAYGXr29QvzgWL2-4CSolLFs,680
|
| 131 |
+
cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-313.pyc,,
|
| 132 |
+
cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-313.pyc,,
|
| 133 |
+
cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-313.pyc,,
|
| 134 |
+
cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-313.pyc,,
|
| 135 |
+
cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-313.pyc,,
|
| 136 |
+
cryptography/hazmat/primitives/ciphers/aead.py,sha256=Fzlyx7w8KYQakzDp1zWgJnIr62zgZrgVh1u2h4exB54,634
|
| 137 |
+
cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=IGfCycYJOh1TvoI34rfw8KfJ_xmt1p_DpBht2Fghceo,3496
|
| 138 |
+
cryptography/hazmat/primitives/ciphers/base.py,sha256=aBC7HHBBoixebmparVr0UlODs3VD0A7B6oz_AaRjDv8,4253
|
| 139 |
+
cryptography/hazmat/primitives/ciphers/modes.py,sha256=H7AZSgJiSTBCi0grL97NxakS950JyFKYLsra8tjHPxQ,6003
|
| 140 |
+
cryptography/hazmat/primitives/cmac.py,sha256=sz_s6H_cYnOvx-VNWdIKhRhe3Ymp8z8J0D3CBqOX3gg,338
|
| 141 |
+
cryptography/hazmat/primitives/constant_time.py,sha256=xdunWT0nf8OvKdcqUhhlFKayGp4_PgVJRU2W1wLSr_A,422
|
| 142 |
+
cryptography/hazmat/primitives/hashes.py,sha256=Srtwgc3_pFvZfIhRj9e1p4Wdi-F_PpleGHlqJARgtOg,7916
|
| 143 |
+
cryptography/hazmat/primitives/hmac.py,sha256=RpB3z9z5skirCQrm7zQbtnp9pLMnAjrlTUvKqF5aDDc,423
|
| 144 |
+
cryptography/hazmat/primitives/hpke.py,sha256=RsHissC5l-dTPn1p2JbcrIrAnDBrLqM5ugBFSGpmKu4,865
|
| 145 |
+
cryptography/hazmat/primitives/kdf/__init__.py,sha256=v3yiYBGU272EojNXbwfYZdbbfI9cVOCCG3nXhTDda3k,1037
|
| 146 |
+
cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-313.pyc,,
|
| 147 |
+
cryptography/hazmat/primitives/kdf/__pycache__/argon2.cpython-313.pyc,,
|
| 148 |
+
cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-313.pyc,,
|
| 149 |
+
cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-313.pyc,,
|
| 150 |
+
cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-313.pyc,,
|
| 151 |
+
cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-313.pyc,,
|
| 152 |
+
cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-313.pyc,,
|
| 153 |
+
cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-313.pyc,,
|
| 154 |
+
cryptography/hazmat/primitives/kdf/argon2.py,sha256=ZJx-enUlAA4o7b46C1wlzG_XAQQW0wIkFgUKkrM_wA8,632
|
| 155 |
+
cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=BFnOKS72txKF0yQvASr0Na7uhzvLHpUCckK5iNAC_aQ,591
|
| 156 |
+
cryptography/hazmat/primitives/kdf/hkdf.py,sha256=M0lAEfRoc4kpp4-nwDj9yB-vNZukIOYEQrUlWsBNn9o,543
|
| 157 |
+
cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=C3w99vjFqhqzAxzFOH4zzvVoHkb5AC6Lz6AEQGV7d_g,737
|
| 158 |
+
cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=Xchkk99s-Mk6b6KSCouqdk8FVyiNVOSFIenmnW3tLgQ,468
|
| 159 |
+
cryptography/hazmat/primitives/kdf/scrypt.py,sha256=XyWUdUUmhuI9V6TqAPOvujCSMGv1XQdg0a21IWCmO-U,590
|
| 160 |
+
cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=QKjhRehuTsAstL384bKfvjuv1yfdamAHVhsBRWen2Vw,456
|
| 161 |
+
cryptography/hazmat/primitives/keywrap.py,sha256=t9t8KZuzZ4Us30ASUhI27MZPPPKy4EERRDkAK-lM7TQ,735
|
| 162 |
+
cryptography/hazmat/primitives/padding.py,sha256=QT-U-NvV2eQGO1wVPbDiNGNSc9keRDS-ig5cQOrLz0E,1865
|
| 163 |
+
cryptography/hazmat/primitives/poly1305.py,sha256=P5EPQV-RB_FJPahpg01u0Ts4S_PnAmsroxIGXbGeRRo,355
|
| 164 |
+
cryptography/hazmat/primitives/serialization/__init__.py,sha256=fXcu49ugL9rxhpRl_wZjnMqE9DCkhCMcsRxfj_qTveg,2217
|
| 165 |
+
cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-313.pyc,,
|
| 166 |
+
cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-313.pyc,,
|
| 167 |
+
cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-313.pyc,,
|
| 168 |
+
cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-313.pyc,,
|
| 169 |
+
cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-313.pyc,,
|
| 170 |
+
cryptography/hazmat/primitives/serialization/base.py,sha256=SABrvo0S86QcoBlj7DpjeLWy2lzQMryiioJ6DrEYQVA,651
|
| 171 |
+
cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=mS9cFNG4afzvseoc5e1MWoY2VskfL8N8Y_OFjl67luY,5104
|
| 172 |
+
cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=mFM7OFuZ8cCxUGUoo4Cof6BnCHc_Ibx1AQjjfxnPlxo,13998
|
| 173 |
+
cryptography/hazmat/primitives/serialization/ssh.py,sha256=FisXe_nUKdPGfqENVhKpmoazmaI8qH8Ge-VGbc0dykw,53592
|
| 174 |
+
cryptography/hazmat/primitives/twofactor/__init__.py,sha256=tmMZGB-g4IU1r7lIFqASU019zr0uPp_wEBYcwdDCKCA,258
|
| 175 |
+
cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-313.pyc,,
|
| 176 |
+
cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-313.pyc,,
|
| 177 |
+
cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-313.pyc,,
|
| 178 |
+
cryptography/hazmat/primitives/twofactor/hotp.py,sha256=ivZo5BrcCGWLsqql4nZV0XXCjyGPi_iHfDFltGlOJwk,3256
|
| 179 |
+
cryptography/hazmat/primitives/twofactor/totp.py,sha256=m5LPpRL00kp4zY8gTjr55Hfz9aMlPS53kHmVkSQCmdY,1652
|
| 180 |
+
cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 181 |
+
cryptography/utils.py,sha256=Ia7S0QZzS0ezwpIHQfY8G1e-o4GjrHijEXXjwAr1iuI,4635
|
| 182 |
+
cryptography/x509/__init__.py,sha256=0KVNTmRtdYNX_i_ewsTNzbTm2e1qyfoOhc4emog5hL4,8253
|
| 183 |
+
cryptography/x509/__pycache__/__init__.cpython-313.pyc,,
|
| 184 |
+
cryptography/x509/__pycache__/base.cpython-313.pyc,,
|
| 185 |
+
cryptography/x509/__pycache__/certificate_transparency.cpython-313.pyc,,
|
| 186 |
+
cryptography/x509/__pycache__/extensions.cpython-313.pyc,,
|
| 187 |
+
cryptography/x509/__pycache__/general_name.cpython-313.pyc,,
|
| 188 |
+
cryptography/x509/__pycache__/name.cpython-313.pyc,,
|
| 189 |
+
cryptography/x509/__pycache__/ocsp.cpython-313.pyc,,
|
| 190 |
+
cryptography/x509/__pycache__/oid.cpython-313.pyc,,
|
| 191 |
+
cryptography/x509/__pycache__/verification.cpython-313.pyc,,
|
| 192 |
+
cryptography/x509/base.py,sha256=UIMKkwmsx10BR3_gnBRBCBTwQp1-O8wkfVm7azH-_mE,28311
|
| 193 |
+
cryptography/x509/certificate_transparency.py,sha256=JqoOIDhlwInrYMFW6IFn77WJ0viF-PB_rlZV3vs9MYc,797
|
| 194 |
+
cryptography/x509/extensions.py,sha256=-iXcwWnIL1zl5Ml3BVJFkEo_71ncXe84ehvqzW1sK4M,77975
|
| 195 |
+
cryptography/x509/general_name.py,sha256=sP_rV11Qlpsk4x3XXGJY_Mv0Q_s9dtjeLckHsjpLQoQ,7836
|
| 196 |
+
cryptography/x509/name.py,sha256=oI2w6VdY8zgw7qpNhlxEx512W0AhWDXQrDYf5EsM2PY,15566
|
| 197 |
+
cryptography/x509/ocsp.py,sha256=Yey6NdFV1MPjop24Mj_VenjEpg3kUaMopSWOK0AbeBs,12699
|
| 198 |
+
cryptography/x509/oid.py,sha256=BUzgXXGVWilkBkdKPTm9R4qElE9gAGHgdYPMZAp7PJo,931
|
| 199 |
+
cryptography/x509/verification.py,sha256=gR2C2c-XZQtblZhT5T5vjSKOtCb74ef2alPVmEcwFlM,958
|
.mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: maturin (1.14.1)
|
| 3 |
+
Root-Is-Purelib: false
|
| 4 |
+
Tag: cp311-abi3-manylinux_2_34_x86_64
|
| 5 |
+
|
.mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
This software is made available under the terms of *either* of the licenses
|
| 2 |
+
found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made
|
| 3 |
+
under the terms of *both* these licenses.
|
.mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/licenses/LICENSE.APACHE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
https://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
| 178 |
+
|
| 179 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 180 |
+
|
| 181 |
+
To apply the Apache License to your work, attach the following
|
| 182 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 183 |
+
replaced with your own identifying information. (Don't include
|
| 184 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 185 |
+
comment syntax for the file format. We also recommend that a
|
| 186 |
+
file or class name and description of purpose be included on the
|
| 187 |
+
same "printed page" as the copyright notice for easier
|
| 188 |
+
identification within third-party archives.
|
| 189 |
+
|
| 190 |
+
Copyright [yyyy] [name of copyright owner]
|
| 191 |
+
|
| 192 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 193 |
+
you may not use this file except in compliance with the License.
|
| 194 |
+
You may obtain a copy of the License at
|
| 195 |
+
|
| 196 |
+
https://www.apache.org/licenses/LICENSE-2.0
|
| 197 |
+
|
| 198 |
+
Unless required by applicable law or agreed to in writing, software
|
| 199 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 200 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 201 |
+
See the License for the specific language governing permissions and
|
| 202 |
+
limitations under the License.
|
.mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/licenses/LICENSE.BSD
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) Individual contributors.
|
| 2 |
+
All rights reserved.
|
| 3 |
+
|
| 4 |
+
Redistribution and use in source and binary forms, with or without
|
| 5 |
+
modification, are permitted provided that the following conditions are met:
|
| 6 |
+
|
| 7 |
+
1. Redistributions of source code must retain the above copyright notice,
|
| 8 |
+
this list of conditions and the following disclaimer.
|
| 9 |
+
|
| 10 |
+
2. Redistributions in binary form must reproduce the above copyright
|
| 11 |
+
notice, this list of conditions and the following disclaimer in the
|
| 12 |
+
documentation and/or other materials provided with the distribution.
|
| 13 |
+
|
| 14 |
+
3. Neither the name of PyCA Cryptography nor the names of its contributors
|
| 15 |
+
may be used to endorse or promote products derived from this software
|
| 16 |
+
without specific prior written permission.
|
| 17 |
+
|
| 18 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
| 19 |
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| 20 |
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| 21 |
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
| 22 |
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
| 23 |
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 24 |
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
| 25 |
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 26 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 27 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
.mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json
ADDED
|
@@ -0,0 +1,1365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bomFormat": "CycloneDX",
|
| 3 |
+
"specVersion": "1.5",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"serialNumber": "urn:uuid:4de2bf3d-09cb-4570-8848-6e42c6141788",
|
| 6 |
+
"metadata": {
|
| 7 |
+
"timestamp": "2026-07-31T14:08:58.786399420Z",
|
| 8 |
+
"tools": [
|
| 9 |
+
{
|
| 10 |
+
"vendor": "CycloneDX",
|
| 11 |
+
"name": "cargo-cyclonedx",
|
| 12 |
+
"version": "0.5.9"
|
| 13 |
+
}
|
| 14 |
+
],
|
| 15 |
+
"authors": [
|
| 16 |
+
{
|
| 17 |
+
"name": "The cryptography developers",
|
| 18 |
+
"email": "cryptography-dev@python.org"
|
| 19 |
+
}
|
| 20 |
+
],
|
| 21 |
+
"component": {
|
| 22 |
+
"type": "library",
|
| 23 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust#cryptography-rust@0.50.0",
|
| 24 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 25 |
+
"name": "cryptography-rust",
|
| 26 |
+
"version": "0.50.0",
|
| 27 |
+
"scope": "required",
|
| 28 |
+
"licenses": [
|
| 29 |
+
{
|
| 30 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 31 |
+
}
|
| 32 |
+
],
|
| 33 |
+
"purl": "pkg:cargo/cryptography-rust@0.50.0?download_url=file://.",
|
| 34 |
+
"components": [
|
| 35 |
+
{
|
| 36 |
+
"type": "library",
|
| 37 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust#cryptography-rust@0.50.0 bin-target-0",
|
| 38 |
+
"name": "cryptography_rust",
|
| 39 |
+
"version": "0.50.0",
|
| 40 |
+
"purl": "pkg:cargo/cryptography-rust@0.50.0?download_url=file://.#src/lib.rs"
|
| 41 |
+
}
|
| 42 |
+
]
|
| 43 |
+
},
|
| 44 |
+
"properties": [
|
| 45 |
+
{
|
| 46 |
+
"name": "cdx:rustc:sbom:target:all_targets",
|
| 47 |
+
"value": "true"
|
| 48 |
+
}
|
| 49 |
+
]
|
| 50 |
+
},
|
| 51 |
+
"components": [
|
| 52 |
+
{
|
| 53 |
+
"type": "library",
|
| 54 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-cffi#0.50.0",
|
| 55 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 56 |
+
"name": "cryptography-cffi",
|
| 57 |
+
"version": "0.50.0",
|
| 58 |
+
"scope": "required",
|
| 59 |
+
"licenses": [
|
| 60 |
+
{
|
| 61 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 62 |
+
}
|
| 63 |
+
],
|
| 64 |
+
"purl": "pkg:cargo/cryptography-cffi@0.50.0?download_url=file://cryptography-cffi"
|
| 65 |
+
},
|
| 66 |
+
{
|
| 67 |
+
"type": "library",
|
| 68 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-crypto#0.50.0",
|
| 69 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 70 |
+
"name": "cryptography-crypto",
|
| 71 |
+
"version": "0.50.0",
|
| 72 |
+
"scope": "required",
|
| 73 |
+
"licenses": [
|
| 74 |
+
{
|
| 75 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 76 |
+
}
|
| 77 |
+
],
|
| 78 |
+
"purl": "pkg:cargo/cryptography-crypto@0.50.0?download_url=file://cryptography-crypto"
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"type": "library",
|
| 82 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-keepalive#0.50.0",
|
| 83 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 84 |
+
"name": "cryptography-keepalive",
|
| 85 |
+
"version": "0.50.0",
|
| 86 |
+
"scope": "required",
|
| 87 |
+
"licenses": [
|
| 88 |
+
{
|
| 89 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 90 |
+
}
|
| 91 |
+
],
|
| 92 |
+
"purl": "pkg:cargo/cryptography-keepalive@0.50.0?download_url=file://cryptography-keepalive"
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"type": "library",
|
| 96 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-key-parsing#0.50.0",
|
| 97 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 98 |
+
"name": "cryptography-key-parsing",
|
| 99 |
+
"version": "0.50.0",
|
| 100 |
+
"scope": "required",
|
| 101 |
+
"licenses": [
|
| 102 |
+
{
|
| 103 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 104 |
+
}
|
| 105 |
+
],
|
| 106 |
+
"purl": "pkg:cargo/cryptography-key-parsing@0.50.0?download_url=file://cryptography-key-parsing"
|
| 107 |
+
},
|
| 108 |
+
{
|
| 109 |
+
"type": "library",
|
| 110 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-openssl#0.50.0",
|
| 111 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 112 |
+
"name": "cryptography-openssl",
|
| 113 |
+
"version": "0.50.0",
|
| 114 |
+
"scope": "required",
|
| 115 |
+
"licenses": [
|
| 116 |
+
{
|
| 117 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 118 |
+
}
|
| 119 |
+
],
|
| 120 |
+
"purl": "pkg:cargo/cryptography-openssl@0.50.0?download_url=file://cryptography-openssl"
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"type": "library",
|
| 124 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-x509#0.50.0",
|
| 125 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 126 |
+
"name": "cryptography-x509",
|
| 127 |
+
"version": "0.50.0",
|
| 128 |
+
"scope": "required",
|
| 129 |
+
"licenses": [
|
| 130 |
+
{
|
| 131 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 132 |
+
}
|
| 133 |
+
],
|
| 134 |
+
"purl": "pkg:cargo/cryptography-x509@0.50.0?download_url=file://cryptography-x509"
|
| 135 |
+
},
|
| 136 |
+
{
|
| 137 |
+
"type": "library",
|
| 138 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-x509-verification#0.50.0",
|
| 139 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 140 |
+
"name": "cryptography-x509-verification",
|
| 141 |
+
"version": "0.50.0",
|
| 142 |
+
"scope": "required",
|
| 143 |
+
"licenses": [
|
| 144 |
+
{
|
| 145 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 146 |
+
}
|
| 147 |
+
],
|
| 148 |
+
"purl": "pkg:cargo/cryptography-x509-verification@0.50.0?download_url=file://cryptography-x509-verification"
|
| 149 |
+
},
|
| 150 |
+
{
|
| 151 |
+
"type": "library",
|
| 152 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 153 |
+
"author": "Alex Gaynor <alex.gaynor@gmail.com>",
|
| 154 |
+
"name": "asn1",
|
| 155 |
+
"version": "0.24.1",
|
| 156 |
+
"description": "ASN.1 (DER) parser and writer for Rust.",
|
| 157 |
+
"scope": "required",
|
| 158 |
+
"hashes": [
|
| 159 |
+
{
|
| 160 |
+
"alg": "SHA-256",
|
| 161 |
+
"content": "c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1"
|
| 162 |
+
}
|
| 163 |
+
],
|
| 164 |
+
"licenses": [
|
| 165 |
+
{
|
| 166 |
+
"expression": "BSD-3-Clause"
|
| 167 |
+
}
|
| 168 |
+
],
|
| 169 |
+
"purl": "pkg:cargo/asn1@0.24.1",
|
| 170 |
+
"externalReferences": [
|
| 171 |
+
{
|
| 172 |
+
"type": "vcs",
|
| 173 |
+
"url": "https://github.com/alex/rust-asn1"
|
| 174 |
+
}
|
| 175 |
+
]
|
| 176 |
+
},
|
| 177 |
+
{
|
| 178 |
+
"type": "library",
|
| 179 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1",
|
| 180 |
+
"author": "Alex Gaynor <alex.gaynor@gmail.com>",
|
| 181 |
+
"name": "asn1_derive",
|
| 182 |
+
"version": "0.24.1",
|
| 183 |
+
"description": "#[derive] support for asn1",
|
| 184 |
+
"scope": "required",
|
| 185 |
+
"hashes": [
|
| 186 |
+
{
|
| 187 |
+
"alg": "SHA-256",
|
| 188 |
+
"content": "909e307f1cc32bb8bccbd98f446e6d1bf03fa30f7b53a4337da7181ad30fa11a"
|
| 189 |
+
}
|
| 190 |
+
],
|
| 191 |
+
"licenses": [
|
| 192 |
+
{
|
| 193 |
+
"expression": "BSD-3-Clause"
|
| 194 |
+
}
|
| 195 |
+
],
|
| 196 |
+
"purl": "pkg:cargo/asn1_derive@0.24.1",
|
| 197 |
+
"externalReferences": [
|
| 198 |
+
{
|
| 199 |
+
"type": "vcs",
|
| 200 |
+
"url": "https://github.com/alex/rust-asn1"
|
| 201 |
+
}
|
| 202 |
+
]
|
| 203 |
+
},
|
| 204 |
+
{
|
| 205 |
+
"type": "library",
|
| 206 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.23.0",
|
| 207 |
+
"author": "Marshall Pierce <marshall@mpierce.org>",
|
| 208 |
+
"name": "base64",
|
| 209 |
+
"version": "0.23.0",
|
| 210 |
+
"description": "encodes and decodes base64 as bytes or utf8",
|
| 211 |
+
"scope": "required",
|
| 212 |
+
"hashes": [
|
| 213 |
+
{
|
| 214 |
+
"alg": "SHA-256",
|
| 215 |
+
"content": "b25655df2c3cdd83c5e5b293b88acd880332b2ddadd7c30ac43144fdc0033da9"
|
| 216 |
+
}
|
| 217 |
+
],
|
| 218 |
+
"licenses": [
|
| 219 |
+
{
|
| 220 |
+
"expression": "MIT OR Apache-2.0"
|
| 221 |
+
}
|
| 222 |
+
],
|
| 223 |
+
"purl": "pkg:cargo/base64@0.23.0",
|
| 224 |
+
"externalReferences": [
|
| 225 |
+
{
|
| 226 |
+
"type": "documentation",
|
| 227 |
+
"url": "https://docs.rs/base64"
|
| 228 |
+
},
|
| 229 |
+
{
|
| 230 |
+
"type": "vcs",
|
| 231 |
+
"url": "https://github.com/marshallpierce/rust-base64"
|
| 232 |
+
}
|
| 233 |
+
]
|
| 234 |
+
},
|
| 235 |
+
{
|
| 236 |
+
"type": "library",
|
| 237 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.1",
|
| 238 |
+
"author": "The Rust Project Developers",
|
| 239 |
+
"name": "bitflags",
|
| 240 |
+
"version": "2.13.1",
|
| 241 |
+
"description": "A macro to generate structures which behave like bitflags. ",
|
| 242 |
+
"scope": "required",
|
| 243 |
+
"hashes": [
|
| 244 |
+
{
|
| 245 |
+
"alg": "SHA-256",
|
| 246 |
+
"content": "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
| 247 |
+
}
|
| 248 |
+
],
|
| 249 |
+
"licenses": [
|
| 250 |
+
{
|
| 251 |
+
"expression": "MIT OR Apache-2.0"
|
| 252 |
+
}
|
| 253 |
+
],
|
| 254 |
+
"purl": "pkg:cargo/bitflags@2.13.1",
|
| 255 |
+
"externalReferences": [
|
| 256 |
+
{
|
| 257 |
+
"type": "documentation",
|
| 258 |
+
"url": "https://docs.rs/bitflags"
|
| 259 |
+
},
|
| 260 |
+
{
|
| 261 |
+
"type": "website",
|
| 262 |
+
"url": "https://github.com/bitflags/bitflags"
|
| 263 |
+
},
|
| 264 |
+
{
|
| 265 |
+
"type": "vcs",
|
| 266 |
+
"url": "https://github.com/bitflags/bitflags"
|
| 267 |
+
}
|
| 268 |
+
]
|
| 269 |
+
},
|
| 270 |
+
{
|
| 271 |
+
"type": "library",
|
| 272 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.4.0",
|
| 273 |
+
"author": "Alex Crichton <alex@alexcrichton.com>",
|
| 274 |
+
"name": "cc",
|
| 275 |
+
"version": "1.4.0",
|
| 276 |
+
"description": "A build-time dependency for Cargo build scripts to assist in invoking the native C compiler to compile native C code into a static archive to be linked into Rust code. ",
|
| 277 |
+
"scope": "excluded",
|
| 278 |
+
"hashes": [
|
| 279 |
+
{
|
| 280 |
+
"alg": "SHA-256",
|
| 281 |
+
"content": "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
|
| 282 |
+
}
|
| 283 |
+
],
|
| 284 |
+
"licenses": [
|
| 285 |
+
{
|
| 286 |
+
"expression": "MIT OR Apache-2.0"
|
| 287 |
+
}
|
| 288 |
+
],
|
| 289 |
+
"purl": "pkg:cargo/cc@1.4.0",
|
| 290 |
+
"externalReferences": [
|
| 291 |
+
{
|
| 292 |
+
"type": "documentation",
|
| 293 |
+
"url": "https://docs.rs/cc"
|
| 294 |
+
},
|
| 295 |
+
{
|
| 296 |
+
"type": "website",
|
| 297 |
+
"url": "https://github.com/rust-lang/cc-rs"
|
| 298 |
+
},
|
| 299 |
+
{
|
| 300 |
+
"type": "vcs",
|
| 301 |
+
"url": "https://github.com/rust-lang/cc-rs"
|
| 302 |
+
}
|
| 303 |
+
]
|
| 304 |
+
},
|
| 305 |
+
{
|
| 306 |
+
"type": "library",
|
| 307 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 308 |
+
"author": "Alex Crichton <alex@alexcrichton.com>",
|
| 309 |
+
"name": "cfg-if",
|
| 310 |
+
"version": "1.0.4",
|
| 311 |
+
"description": "A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted. ",
|
| 312 |
+
"scope": "required",
|
| 313 |
+
"hashes": [
|
| 314 |
+
{
|
| 315 |
+
"alg": "SHA-256",
|
| 316 |
+
"content": "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
| 317 |
+
}
|
| 318 |
+
],
|
| 319 |
+
"licenses": [
|
| 320 |
+
{
|
| 321 |
+
"expression": "MIT OR Apache-2.0"
|
| 322 |
+
}
|
| 323 |
+
],
|
| 324 |
+
"purl": "pkg:cargo/cfg-if@1.0.4",
|
| 325 |
+
"externalReferences": [
|
| 326 |
+
{
|
| 327 |
+
"type": "vcs",
|
| 328 |
+
"url": "https://github.com/rust-lang/cfg-if"
|
| 329 |
+
}
|
| 330 |
+
]
|
| 331 |
+
},
|
| 332 |
+
{
|
| 333 |
+
"type": "library",
|
| 334 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9",
|
| 335 |
+
"name": "find-msvc-tools",
|
| 336 |
+
"version": "0.1.9",
|
| 337 |
+
"description": "Find windows-specific tools, read MSVC versions from the registry and from COM interfaces",
|
| 338 |
+
"scope": "excluded",
|
| 339 |
+
"hashes": [
|
| 340 |
+
{
|
| 341 |
+
"alg": "SHA-256",
|
| 342 |
+
"content": "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
| 343 |
+
}
|
| 344 |
+
],
|
| 345 |
+
"licenses": [
|
| 346 |
+
{
|
| 347 |
+
"expression": "MIT OR Apache-2.0"
|
| 348 |
+
}
|
| 349 |
+
],
|
| 350 |
+
"purl": "pkg:cargo/find-msvc-tools@0.1.9",
|
| 351 |
+
"externalReferences": [
|
| 352 |
+
{
|
| 353 |
+
"type": "documentation",
|
| 354 |
+
"url": "https://docs.rs/find-msvc-tools"
|
| 355 |
+
},
|
| 356 |
+
{
|
| 357 |
+
"type": "vcs",
|
| 358 |
+
"url": "https://github.com/rust-lang/cc-rs"
|
| 359 |
+
}
|
| 360 |
+
]
|
| 361 |
+
},
|
| 362 |
+
{
|
| 363 |
+
"type": "library",
|
| 364 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1",
|
| 365 |
+
"author": "Steven Fackler <sfackler@gmail.com>",
|
| 366 |
+
"name": "foreign-types-shared",
|
| 367 |
+
"version": "0.1.1",
|
| 368 |
+
"description": "An internal crate used by foreign-types",
|
| 369 |
+
"scope": "required",
|
| 370 |
+
"hashes": [
|
| 371 |
+
{
|
| 372 |
+
"alg": "SHA-256",
|
| 373 |
+
"content": "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
| 374 |
+
}
|
| 375 |
+
],
|
| 376 |
+
"licenses": [
|
| 377 |
+
{
|
| 378 |
+
"expression": "MIT OR Apache-2.0"
|
| 379 |
+
}
|
| 380 |
+
],
|
| 381 |
+
"purl": "pkg:cargo/foreign-types-shared@0.1.1",
|
| 382 |
+
"externalReferences": [
|
| 383 |
+
{
|
| 384 |
+
"type": "vcs",
|
| 385 |
+
"url": "https://github.com/sfackler/foreign-types"
|
| 386 |
+
}
|
| 387 |
+
]
|
| 388 |
+
},
|
| 389 |
+
{
|
| 390 |
+
"type": "library",
|
| 391 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2",
|
| 392 |
+
"author": "Steven Fackler <sfackler@gmail.com>",
|
| 393 |
+
"name": "foreign-types",
|
| 394 |
+
"version": "0.3.2",
|
| 395 |
+
"description": "A framework for Rust wrappers over C APIs",
|
| 396 |
+
"scope": "required",
|
| 397 |
+
"hashes": [
|
| 398 |
+
{
|
| 399 |
+
"alg": "SHA-256",
|
| 400 |
+
"content": "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
| 401 |
+
}
|
| 402 |
+
],
|
| 403 |
+
"licenses": [
|
| 404 |
+
{
|
| 405 |
+
"expression": "MIT OR Apache-2.0"
|
| 406 |
+
}
|
| 407 |
+
],
|
| 408 |
+
"purl": "pkg:cargo/foreign-types@0.3.2",
|
| 409 |
+
"externalReferences": [
|
| 410 |
+
{
|
| 411 |
+
"type": "vcs",
|
| 412 |
+
"url": "https://github.com/sfackler/foreign-types"
|
| 413 |
+
}
|
| 414 |
+
]
|
| 415 |
+
},
|
| 416 |
+
{
|
| 417 |
+
"type": "library",
|
| 418 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0",
|
| 419 |
+
"name": "heck",
|
| 420 |
+
"version": "0.5.0",
|
| 421 |
+
"description": "heck is a case conversion library.",
|
| 422 |
+
"scope": "required",
|
| 423 |
+
"hashes": [
|
| 424 |
+
{
|
| 425 |
+
"alg": "SHA-256",
|
| 426 |
+
"content": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
| 427 |
+
}
|
| 428 |
+
],
|
| 429 |
+
"licenses": [
|
| 430 |
+
{
|
| 431 |
+
"expression": "MIT OR Apache-2.0"
|
| 432 |
+
}
|
| 433 |
+
],
|
| 434 |
+
"purl": "pkg:cargo/heck@0.5.0",
|
| 435 |
+
"externalReferences": [
|
| 436 |
+
{
|
| 437 |
+
"type": "vcs",
|
| 438 |
+
"url": "https://github.com/withoutboats/heck"
|
| 439 |
+
}
|
| 440 |
+
]
|
| 441 |
+
},
|
| 442 |
+
{
|
| 443 |
+
"type": "library",
|
| 444 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18",
|
| 445 |
+
"author": "David Tolnay <dtolnay@gmail.com>",
|
| 446 |
+
"name": "itoa",
|
| 447 |
+
"version": "1.0.18",
|
| 448 |
+
"description": "Fast integer primitive to string conversion",
|
| 449 |
+
"scope": "required",
|
| 450 |
+
"hashes": [
|
| 451 |
+
{
|
| 452 |
+
"alg": "SHA-256",
|
| 453 |
+
"content": "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
| 454 |
+
}
|
| 455 |
+
],
|
| 456 |
+
"licenses": [
|
| 457 |
+
{
|
| 458 |
+
"expression": "MIT OR Apache-2.0"
|
| 459 |
+
}
|
| 460 |
+
],
|
| 461 |
+
"purl": "pkg:cargo/itoa@1.0.18",
|
| 462 |
+
"externalReferences": [
|
| 463 |
+
{
|
| 464 |
+
"type": "documentation",
|
| 465 |
+
"url": "https://docs.rs/itoa"
|
| 466 |
+
},
|
| 467 |
+
{
|
| 468 |
+
"type": "vcs",
|
| 469 |
+
"url": "https://github.com/dtolnay/itoa"
|
| 470 |
+
}
|
| 471 |
+
]
|
| 472 |
+
},
|
| 473 |
+
{
|
| 474 |
+
"type": "library",
|
| 475 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189",
|
| 476 |
+
"name": "libc",
|
| 477 |
+
"version": "0.2.189",
|
| 478 |
+
"description": "Raw FFI bindings to platform libraries like libc.",
|
| 479 |
+
"scope": "required",
|
| 480 |
+
"hashes": [
|
| 481 |
+
{
|
| 482 |
+
"alg": "SHA-256",
|
| 483 |
+
"content": "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
| 484 |
+
}
|
| 485 |
+
],
|
| 486 |
+
"licenses": [
|
| 487 |
+
{
|
| 488 |
+
"expression": "MIT OR Apache-2.0"
|
| 489 |
+
}
|
| 490 |
+
],
|
| 491 |
+
"purl": "pkg:cargo/libc@0.2.189",
|
| 492 |
+
"externalReferences": [
|
| 493 |
+
{
|
| 494 |
+
"type": "vcs",
|
| 495 |
+
"url": "https://github.com/rust-lang/libc"
|
| 496 |
+
}
|
| 497 |
+
]
|
| 498 |
+
},
|
| 499 |
+
{
|
| 500 |
+
"type": "library",
|
| 501 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4",
|
| 502 |
+
"author": "Aleksey Kladov <aleksey.kladov@gmail.com>",
|
| 503 |
+
"name": "once_cell",
|
| 504 |
+
"version": "1.21.4",
|
| 505 |
+
"description": "Single assignment cells and lazy values.",
|
| 506 |
+
"scope": "required",
|
| 507 |
+
"hashes": [
|
| 508 |
+
{
|
| 509 |
+
"alg": "SHA-256",
|
| 510 |
+
"content": "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
| 511 |
+
}
|
| 512 |
+
],
|
| 513 |
+
"licenses": [
|
| 514 |
+
{
|
| 515 |
+
"expression": "MIT OR Apache-2.0"
|
| 516 |
+
}
|
| 517 |
+
],
|
| 518 |
+
"purl": "pkg:cargo/once_cell@1.21.4",
|
| 519 |
+
"externalReferences": [
|
| 520 |
+
{
|
| 521 |
+
"type": "documentation",
|
| 522 |
+
"url": "https://docs.rs/once_cell"
|
| 523 |
+
},
|
| 524 |
+
{
|
| 525 |
+
"type": "vcs",
|
| 526 |
+
"url": "https://github.com/matklad/once_cell"
|
| 527 |
+
}
|
| 528 |
+
]
|
| 529 |
+
},
|
| 530 |
+
{
|
| 531 |
+
"type": "library",
|
| 532 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1",
|
| 533 |
+
"name": "openssl-macros",
|
| 534 |
+
"version": "0.1.1",
|
| 535 |
+
"description": "Internal macros used by the openssl crate.",
|
| 536 |
+
"scope": "required",
|
| 537 |
+
"hashes": [
|
| 538 |
+
{
|
| 539 |
+
"alg": "SHA-256",
|
| 540 |
+
"content": "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
| 541 |
+
}
|
| 542 |
+
],
|
| 543 |
+
"licenses": [
|
| 544 |
+
{
|
| 545 |
+
"expression": "MIT OR Apache-2.0"
|
| 546 |
+
}
|
| 547 |
+
],
|
| 548 |
+
"purl": "pkg:cargo/openssl-macros@0.1.1"
|
| 549 |
+
},
|
| 550 |
+
{
|
| 551 |
+
"type": "library",
|
| 552 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 553 |
+
"author": "Alex Crichton <alex@alexcrichton.com>, Steven Fackler <sfackler@gmail.com>",
|
| 554 |
+
"name": "openssl-sys",
|
| 555 |
+
"version": "0.9.117",
|
| 556 |
+
"description": "FFI bindings to OpenSSL",
|
| 557 |
+
"scope": "required",
|
| 558 |
+
"hashes": [
|
| 559 |
+
{
|
| 560 |
+
"alg": "SHA-256",
|
| 561 |
+
"content": "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695"
|
| 562 |
+
}
|
| 563 |
+
],
|
| 564 |
+
"licenses": [
|
| 565 |
+
{
|
| 566 |
+
"expression": "MIT"
|
| 567 |
+
}
|
| 568 |
+
],
|
| 569 |
+
"purl": "pkg:cargo/openssl-sys@0.9.117",
|
| 570 |
+
"externalReferences": [
|
| 571 |
+
{
|
| 572 |
+
"type": "other",
|
| 573 |
+
"url": "openssl"
|
| 574 |
+
},
|
| 575 |
+
{
|
| 576 |
+
"type": "vcs",
|
| 577 |
+
"url": "https://github.com/rust-openssl/rust-openssl"
|
| 578 |
+
}
|
| 579 |
+
]
|
| 580 |
+
},
|
| 581 |
+
{
|
| 582 |
+
"type": "library",
|
| 583 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 584 |
+
"author": "Steven Fackler <sfackler@gmail.com>",
|
| 585 |
+
"name": "openssl",
|
| 586 |
+
"version": "0.10.81",
|
| 587 |
+
"description": "OpenSSL bindings",
|
| 588 |
+
"scope": "required",
|
| 589 |
+
"hashes": [
|
| 590 |
+
{
|
| 591 |
+
"alg": "SHA-256",
|
| 592 |
+
"content": "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45"
|
| 593 |
+
}
|
| 594 |
+
],
|
| 595 |
+
"licenses": [
|
| 596 |
+
{
|
| 597 |
+
"expression": "Apache-2.0"
|
| 598 |
+
}
|
| 599 |
+
],
|
| 600 |
+
"purl": "pkg:cargo/openssl@0.10.81",
|
| 601 |
+
"externalReferences": [
|
| 602 |
+
{
|
| 603 |
+
"type": "vcs",
|
| 604 |
+
"url": "https://github.com/rust-openssl/rust-openssl"
|
| 605 |
+
}
|
| 606 |
+
]
|
| 607 |
+
},
|
| 608 |
+
{
|
| 609 |
+
"type": "library",
|
| 610 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pem@4.0.0",
|
| 611 |
+
"author": "Jonathan Creekmore <jonathan@thecreekmores.org>",
|
| 612 |
+
"name": "pem",
|
| 613 |
+
"version": "4.0.0",
|
| 614 |
+
"description": "Parse and encode PEM-encoded data.",
|
| 615 |
+
"scope": "required",
|
| 616 |
+
"hashes": [
|
| 617 |
+
{
|
| 618 |
+
"alg": "SHA-256",
|
| 619 |
+
"content": "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120"
|
| 620 |
+
}
|
| 621 |
+
],
|
| 622 |
+
"licenses": [
|
| 623 |
+
{
|
| 624 |
+
"expression": "MIT"
|
| 625 |
+
}
|
| 626 |
+
],
|
| 627 |
+
"purl": "pkg:cargo/pem@4.0.0",
|
| 628 |
+
"externalReferences": [
|
| 629 |
+
{
|
| 630 |
+
"type": "documentation",
|
| 631 |
+
"url": "https://docs.rs/pem/"
|
| 632 |
+
},
|
| 633 |
+
{
|
| 634 |
+
"type": "website",
|
| 635 |
+
"url": "https://github.com/jcreekmore/pem-rs.git"
|
| 636 |
+
},
|
| 637 |
+
{
|
| 638 |
+
"type": "vcs",
|
| 639 |
+
"url": "https://github.com/jcreekmore/pem-rs.git"
|
| 640 |
+
}
|
| 641 |
+
]
|
| 642 |
+
},
|
| 643 |
+
{
|
| 644 |
+
"type": "library",
|
| 645 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33",
|
| 646 |
+
"author": "Alex Crichton <alex@alexcrichton.com>",
|
| 647 |
+
"name": "pkg-config",
|
| 648 |
+
"version": "0.3.33",
|
| 649 |
+
"description": "A library to run the pkg-config system tool at build time in order to be used in Cargo build scripts. ",
|
| 650 |
+
"scope": "excluded",
|
| 651 |
+
"hashes": [
|
| 652 |
+
{
|
| 653 |
+
"alg": "SHA-256",
|
| 654 |
+
"content": "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
| 655 |
+
}
|
| 656 |
+
],
|
| 657 |
+
"licenses": [
|
| 658 |
+
{
|
| 659 |
+
"expression": "MIT OR Apache-2.0"
|
| 660 |
+
}
|
| 661 |
+
],
|
| 662 |
+
"purl": "pkg:cargo/pkg-config@0.3.33",
|
| 663 |
+
"externalReferences": [
|
| 664 |
+
{
|
| 665 |
+
"type": "documentation",
|
| 666 |
+
"url": "https://docs.rs/pkg-config"
|
| 667 |
+
},
|
| 668 |
+
{
|
| 669 |
+
"type": "vcs",
|
| 670 |
+
"url": "https://github.com/rust-lang/pkg-config-rs"
|
| 671 |
+
}
|
| 672 |
+
]
|
| 673 |
+
},
|
| 674 |
+
{
|
| 675 |
+
"type": "library",
|
| 676 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.14.0",
|
| 677 |
+
"name": "portable-atomic",
|
| 678 |
+
"version": "1.14.0",
|
| 679 |
+
"description": "Portable atomic types including support for 128-bit atomics, atomic float, etc. ",
|
| 680 |
+
"scope": "required",
|
| 681 |
+
"hashes": [
|
| 682 |
+
{
|
| 683 |
+
"alg": "SHA-256",
|
| 684 |
+
"content": "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
|
| 685 |
+
}
|
| 686 |
+
],
|
| 687 |
+
"licenses": [
|
| 688 |
+
{
|
| 689 |
+
"expression": "Apache-2.0 OR MIT"
|
| 690 |
+
}
|
| 691 |
+
],
|
| 692 |
+
"purl": "pkg:cargo/portable-atomic@1.14.0",
|
| 693 |
+
"externalReferences": [
|
| 694 |
+
{
|
| 695 |
+
"type": "vcs",
|
| 696 |
+
"url": "https://github.com/taiki-e/portable-atomic"
|
| 697 |
+
}
|
| 698 |
+
]
|
| 699 |
+
},
|
| 700 |
+
{
|
| 701 |
+
"type": "library",
|
| 702 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107",
|
| 703 |
+
"author": "David Tolnay <dtolnay@gmail.com>, Alex Crichton <alex@alexcrichton.com>",
|
| 704 |
+
"name": "proc-macro2",
|
| 705 |
+
"version": "1.0.107",
|
| 706 |
+
"description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.",
|
| 707 |
+
"scope": "required",
|
| 708 |
+
"hashes": [
|
| 709 |
+
{
|
| 710 |
+
"alg": "SHA-256",
|
| 711 |
+
"content": "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
| 712 |
+
}
|
| 713 |
+
],
|
| 714 |
+
"licenses": [
|
| 715 |
+
{
|
| 716 |
+
"expression": "MIT OR Apache-2.0"
|
| 717 |
+
}
|
| 718 |
+
],
|
| 719 |
+
"purl": "pkg:cargo/proc-macro2@1.0.107",
|
| 720 |
+
"externalReferences": [
|
| 721 |
+
{
|
| 722 |
+
"type": "documentation",
|
| 723 |
+
"url": "https://docs.rs/proc-macro2"
|
| 724 |
+
},
|
| 725 |
+
{
|
| 726 |
+
"type": "vcs",
|
| 727 |
+
"url": "https://github.com/dtolnay/proc-macro2"
|
| 728 |
+
}
|
| 729 |
+
]
|
| 730 |
+
},
|
| 731 |
+
{
|
| 732 |
+
"type": "library",
|
| 733 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0",
|
| 734 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 735 |
+
"name": "pyo3-build-config",
|
| 736 |
+
"version": "0.29.0",
|
| 737 |
+
"description": "Build configuration for the PyO3 ecosystem",
|
| 738 |
+
"scope": "excluded",
|
| 739 |
+
"hashes": [
|
| 740 |
+
{
|
| 741 |
+
"alg": "SHA-256",
|
| 742 |
+
"content": "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
|
| 743 |
+
}
|
| 744 |
+
],
|
| 745 |
+
"licenses": [
|
| 746 |
+
{
|
| 747 |
+
"expression": "MIT OR Apache-2.0"
|
| 748 |
+
}
|
| 749 |
+
],
|
| 750 |
+
"purl": "pkg:cargo/pyo3-build-config@0.29.0",
|
| 751 |
+
"externalReferences": [
|
| 752 |
+
{
|
| 753 |
+
"type": "website",
|
| 754 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 755 |
+
},
|
| 756 |
+
{
|
| 757 |
+
"type": "vcs",
|
| 758 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 759 |
+
}
|
| 760 |
+
]
|
| 761 |
+
},
|
| 762 |
+
{
|
| 763 |
+
"type": "library",
|
| 764 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0",
|
| 765 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 766 |
+
"name": "pyo3-ffi",
|
| 767 |
+
"version": "0.29.0",
|
| 768 |
+
"description": "Python-API bindings for the PyO3 ecosystem",
|
| 769 |
+
"scope": "required",
|
| 770 |
+
"hashes": [
|
| 771 |
+
{
|
| 772 |
+
"alg": "SHA-256",
|
| 773 |
+
"content": "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
|
| 774 |
+
}
|
| 775 |
+
],
|
| 776 |
+
"licenses": [
|
| 777 |
+
{
|
| 778 |
+
"expression": "MIT OR Apache-2.0"
|
| 779 |
+
}
|
| 780 |
+
],
|
| 781 |
+
"purl": "pkg:cargo/pyo3-ffi@0.29.0",
|
| 782 |
+
"externalReferences": [
|
| 783 |
+
{
|
| 784 |
+
"type": "website",
|
| 785 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 786 |
+
},
|
| 787 |
+
{
|
| 788 |
+
"type": "other",
|
| 789 |
+
"url": "python"
|
| 790 |
+
},
|
| 791 |
+
{
|
| 792 |
+
"type": "vcs",
|
| 793 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 794 |
+
}
|
| 795 |
+
]
|
| 796 |
+
},
|
| 797 |
+
{
|
| 798 |
+
"type": "library",
|
| 799 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0",
|
| 800 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 801 |
+
"name": "pyo3-macros-backend",
|
| 802 |
+
"version": "0.29.0",
|
| 803 |
+
"description": "Code generation for PyO3 package",
|
| 804 |
+
"scope": "required",
|
| 805 |
+
"hashes": [
|
| 806 |
+
{
|
| 807 |
+
"alg": "SHA-256",
|
| 808 |
+
"content": "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
|
| 809 |
+
}
|
| 810 |
+
],
|
| 811 |
+
"licenses": [
|
| 812 |
+
{
|
| 813 |
+
"expression": "MIT OR Apache-2.0"
|
| 814 |
+
}
|
| 815 |
+
],
|
| 816 |
+
"purl": "pkg:cargo/pyo3-macros-backend@0.29.0",
|
| 817 |
+
"externalReferences": [
|
| 818 |
+
{
|
| 819 |
+
"type": "website",
|
| 820 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 821 |
+
},
|
| 822 |
+
{
|
| 823 |
+
"type": "vcs",
|
| 824 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 825 |
+
}
|
| 826 |
+
]
|
| 827 |
+
},
|
| 828 |
+
{
|
| 829 |
+
"type": "library",
|
| 830 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0",
|
| 831 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 832 |
+
"name": "pyo3-macros",
|
| 833 |
+
"version": "0.29.0",
|
| 834 |
+
"description": "Proc macros for PyO3 package",
|
| 835 |
+
"scope": "required",
|
| 836 |
+
"hashes": [
|
| 837 |
+
{
|
| 838 |
+
"alg": "SHA-256",
|
| 839 |
+
"content": "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
|
| 840 |
+
}
|
| 841 |
+
],
|
| 842 |
+
"licenses": [
|
| 843 |
+
{
|
| 844 |
+
"expression": "MIT OR Apache-2.0"
|
| 845 |
+
}
|
| 846 |
+
],
|
| 847 |
+
"purl": "pkg:cargo/pyo3-macros@0.29.0",
|
| 848 |
+
"externalReferences": [
|
| 849 |
+
{
|
| 850 |
+
"type": "website",
|
| 851 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 852 |
+
},
|
| 853 |
+
{
|
| 854 |
+
"type": "vcs",
|
| 855 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 856 |
+
}
|
| 857 |
+
]
|
| 858 |
+
},
|
| 859 |
+
{
|
| 860 |
+
"type": "library",
|
| 861 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0",
|
| 862 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 863 |
+
"name": "pyo3",
|
| 864 |
+
"version": "0.29.0",
|
| 865 |
+
"description": "Bindings to Python interpreter",
|
| 866 |
+
"scope": "required",
|
| 867 |
+
"hashes": [
|
| 868 |
+
{
|
| 869 |
+
"alg": "SHA-256",
|
| 870 |
+
"content": "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
|
| 871 |
+
}
|
| 872 |
+
],
|
| 873 |
+
"licenses": [
|
| 874 |
+
{
|
| 875 |
+
"expression": "MIT OR Apache-2.0"
|
| 876 |
+
}
|
| 877 |
+
],
|
| 878 |
+
"purl": "pkg:cargo/pyo3@0.29.0",
|
| 879 |
+
"externalReferences": [
|
| 880 |
+
{
|
| 881 |
+
"type": "documentation",
|
| 882 |
+
"url": "https://docs.rs/crate/pyo3/"
|
| 883 |
+
},
|
| 884 |
+
{
|
| 885 |
+
"type": "website",
|
| 886 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 887 |
+
},
|
| 888 |
+
{
|
| 889 |
+
"type": "other",
|
| 890 |
+
"url": "pyo3-python"
|
| 891 |
+
},
|
| 892 |
+
{
|
| 893 |
+
"type": "vcs",
|
| 894 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 895 |
+
}
|
| 896 |
+
]
|
| 897 |
+
},
|
| 898 |
+
{
|
| 899 |
+
"type": "library",
|
| 900 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47",
|
| 901 |
+
"author": "David Tolnay <dtolnay@gmail.com>",
|
| 902 |
+
"name": "quote",
|
| 903 |
+
"version": "1.0.47",
|
| 904 |
+
"description": "Quasi-quoting macro quote!(...)",
|
| 905 |
+
"scope": "required",
|
| 906 |
+
"hashes": [
|
| 907 |
+
{
|
| 908 |
+
"alg": "SHA-256",
|
| 909 |
+
"content": "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
| 910 |
+
}
|
| 911 |
+
],
|
| 912 |
+
"licenses": [
|
| 913 |
+
{
|
| 914 |
+
"expression": "MIT OR Apache-2.0"
|
| 915 |
+
}
|
| 916 |
+
],
|
| 917 |
+
"purl": "pkg:cargo/quote@1.0.47",
|
| 918 |
+
"externalReferences": [
|
| 919 |
+
{
|
| 920 |
+
"type": "documentation",
|
| 921 |
+
"url": "https://docs.rs/quote/"
|
| 922 |
+
},
|
| 923 |
+
{
|
| 924 |
+
"type": "vcs",
|
| 925 |
+
"url": "https://github.com/dtolnay/quote"
|
| 926 |
+
}
|
| 927 |
+
]
|
| 928 |
+
},
|
| 929 |
+
{
|
| 930 |
+
"type": "library",
|
| 931 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.3.0",
|
| 932 |
+
"author": "Lukas Bergdoll <lukas.bergdoll@gmail.com>",
|
| 933 |
+
"name": "self_cell",
|
| 934 |
+
"version": "1.3.0",
|
| 935 |
+
"description": "Safe-to-use proc-macro-free self-referential structs in stable Rust.",
|
| 936 |
+
"scope": "required",
|
| 937 |
+
"hashes": [
|
| 938 |
+
{
|
| 939 |
+
"alg": "SHA-256",
|
| 940 |
+
"content": "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813"
|
| 941 |
+
}
|
| 942 |
+
],
|
| 943 |
+
"licenses": [
|
| 944 |
+
{
|
| 945 |
+
"expression": "Apache-2.0 OR GPL-2.0-only"
|
| 946 |
+
}
|
| 947 |
+
],
|
| 948 |
+
"purl": "pkg:cargo/self_cell@1.3.0",
|
| 949 |
+
"externalReferences": [
|
| 950 |
+
{
|
| 951 |
+
"type": "documentation",
|
| 952 |
+
"url": "https://docs.rs/self_cell"
|
| 953 |
+
},
|
| 954 |
+
{
|
| 955 |
+
"type": "vcs",
|
| 956 |
+
"url": "https://github.com/Voultapher/self_cell"
|
| 957 |
+
}
|
| 958 |
+
]
|
| 959 |
+
},
|
| 960 |
+
{
|
| 961 |
+
"type": "library",
|
| 962 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1",
|
| 963 |
+
"author": "comex <comexk@gmail.com>, Fenhl <fenhl@fenhl.net>, Adrian Taylor <adetaylor@chromium.org>, Alex Touchet <alextouchet@outlook.com>, Daniel Parks <dp+git@oxidized.org>, Garrett Berg <googberg@gmail.com>",
|
| 964 |
+
"name": "shlex",
|
| 965 |
+
"version": "2.0.1",
|
| 966 |
+
"description": "Split a string into shell words, like Python's shlex.",
|
| 967 |
+
"scope": "excluded",
|
| 968 |
+
"hashes": [
|
| 969 |
+
{
|
| 970 |
+
"alg": "SHA-256",
|
| 971 |
+
"content": "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
| 972 |
+
}
|
| 973 |
+
],
|
| 974 |
+
"licenses": [
|
| 975 |
+
{
|
| 976 |
+
"expression": "MIT OR Apache-2.0"
|
| 977 |
+
}
|
| 978 |
+
],
|
| 979 |
+
"purl": "pkg:cargo/shlex@2.0.1",
|
| 980 |
+
"externalReferences": [
|
| 981 |
+
{
|
| 982 |
+
"type": "vcs",
|
| 983 |
+
"url": "https://github.com/comex/rust-shlex"
|
| 984 |
+
}
|
| 985 |
+
]
|
| 986 |
+
},
|
| 987 |
+
{
|
| 988 |
+
"type": "library",
|
| 989 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119",
|
| 990 |
+
"author": "David Tolnay <dtolnay@gmail.com>",
|
| 991 |
+
"name": "syn",
|
| 992 |
+
"version": "2.0.119",
|
| 993 |
+
"description": "Parser for Rust source code",
|
| 994 |
+
"scope": "required",
|
| 995 |
+
"hashes": [
|
| 996 |
+
{
|
| 997 |
+
"alg": "SHA-256",
|
| 998 |
+
"content": "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
| 999 |
+
}
|
| 1000 |
+
],
|
| 1001 |
+
"licenses": [
|
| 1002 |
+
{
|
| 1003 |
+
"expression": "MIT OR Apache-2.0"
|
| 1004 |
+
}
|
| 1005 |
+
],
|
| 1006 |
+
"purl": "pkg:cargo/syn@2.0.119",
|
| 1007 |
+
"externalReferences": [
|
| 1008 |
+
{
|
| 1009 |
+
"type": "documentation",
|
| 1010 |
+
"url": "https://docs.rs/syn"
|
| 1011 |
+
},
|
| 1012 |
+
{
|
| 1013 |
+
"type": "vcs",
|
| 1014 |
+
"url": "https://github.com/dtolnay/syn"
|
| 1015 |
+
}
|
| 1016 |
+
]
|
| 1017 |
+
},
|
| 1018 |
+
{
|
| 1019 |
+
"type": "library",
|
| 1020 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5",
|
| 1021 |
+
"author": "Dan Gohman <sunfish@mozilla.com>",
|
| 1022 |
+
"name": "target-lexicon",
|
| 1023 |
+
"version": "0.13.5",
|
| 1024 |
+
"description": "LLVM target triple types",
|
| 1025 |
+
"scope": "excluded",
|
| 1026 |
+
"hashes": [
|
| 1027 |
+
{
|
| 1028 |
+
"alg": "SHA-256",
|
| 1029 |
+
"content": "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
| 1030 |
+
}
|
| 1031 |
+
],
|
| 1032 |
+
"licenses": [
|
| 1033 |
+
{
|
| 1034 |
+
"expression": "Apache-2.0 WITH LLVM-exception"
|
| 1035 |
+
}
|
| 1036 |
+
],
|
| 1037 |
+
"purl": "pkg:cargo/target-lexicon@0.13.5",
|
| 1038 |
+
"externalReferences": [
|
| 1039 |
+
{
|
| 1040 |
+
"type": "documentation",
|
| 1041 |
+
"url": "https://docs.rs/target-lexicon/"
|
| 1042 |
+
},
|
| 1043 |
+
{
|
| 1044 |
+
"type": "vcs",
|
| 1045 |
+
"url": "https://github.com/bytecodealliance/target-lexicon"
|
| 1046 |
+
}
|
| 1047 |
+
]
|
| 1048 |
+
},
|
| 1049 |
+
{
|
| 1050 |
+
"type": "library",
|
| 1051 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24",
|
| 1052 |
+
"author": "David Tolnay <dtolnay@gmail.com>",
|
| 1053 |
+
"name": "unicode-ident",
|
| 1054 |
+
"version": "1.0.24",
|
| 1055 |
+
"description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
|
| 1056 |
+
"scope": "required",
|
| 1057 |
+
"hashes": [
|
| 1058 |
+
{
|
| 1059 |
+
"alg": "SHA-256",
|
| 1060 |
+
"content": "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
| 1061 |
+
}
|
| 1062 |
+
],
|
| 1063 |
+
"licenses": [
|
| 1064 |
+
{
|
| 1065 |
+
"expression": "(MIT OR Apache-2.0) AND Unicode-3.0"
|
| 1066 |
+
}
|
| 1067 |
+
],
|
| 1068 |
+
"purl": "pkg:cargo/unicode-ident@1.0.24",
|
| 1069 |
+
"externalReferences": [
|
| 1070 |
+
{
|
| 1071 |
+
"type": "documentation",
|
| 1072 |
+
"url": "https://docs.rs/unicode-ident"
|
| 1073 |
+
},
|
| 1074 |
+
{
|
| 1075 |
+
"type": "vcs",
|
| 1076 |
+
"url": "https://github.com/dtolnay/unicode-ident"
|
| 1077 |
+
}
|
| 1078 |
+
]
|
| 1079 |
+
},
|
| 1080 |
+
{
|
| 1081 |
+
"type": "library",
|
| 1082 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15",
|
| 1083 |
+
"author": "Jim McGrath <jimmc2@gmail.com>",
|
| 1084 |
+
"name": "vcpkg",
|
| 1085 |
+
"version": "0.2.15",
|
| 1086 |
+
"description": "A library to find native dependencies in a vcpkg tree at build time in order to be used in Cargo build scripts. ",
|
| 1087 |
+
"scope": "excluded",
|
| 1088 |
+
"hashes": [
|
| 1089 |
+
{
|
| 1090 |
+
"alg": "SHA-256",
|
| 1091 |
+
"content": "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
| 1092 |
+
}
|
| 1093 |
+
],
|
| 1094 |
+
"licenses": [
|
| 1095 |
+
{
|
| 1096 |
+
"expression": "MIT OR Apache-2.0"
|
| 1097 |
+
}
|
| 1098 |
+
],
|
| 1099 |
+
"purl": "pkg:cargo/vcpkg@0.2.15",
|
| 1100 |
+
"externalReferences": [
|
| 1101 |
+
{
|
| 1102 |
+
"type": "documentation",
|
| 1103 |
+
"url": "https://docs.rs/vcpkg"
|
| 1104 |
+
},
|
| 1105 |
+
{
|
| 1106 |
+
"type": "vcs",
|
| 1107 |
+
"url": "https://github.com/mcgoo/vcpkg-rs"
|
| 1108 |
+
}
|
| 1109 |
+
]
|
| 1110 |
+
}
|
| 1111 |
+
],
|
| 1112 |
+
"dependencies": [
|
| 1113 |
+
{
|
| 1114 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust#cryptography-rust@0.50.0",
|
| 1115 |
+
"dependsOn": [
|
| 1116 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 1117 |
+
"registry+https://github.com/rust-lang/crates.io-index#base64@0.23.0",
|
| 1118 |
+
"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 1119 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-cffi#0.50.0",
|
| 1120 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-crypto#0.50.0",
|
| 1121 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-keepalive#0.50.0",
|
| 1122 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-key-parsing#0.50.0",
|
| 1123 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-openssl#0.50.0",
|
| 1124 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-x509#0.50.0",
|
| 1125 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-x509-verification#0.50.0",
|
| 1126 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1",
|
| 1127 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 1128 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 1129 |
+
"registry+https://github.com/rust-lang/crates.io-index#pem@4.0.0",
|
| 1130 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0",
|
| 1131 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0",
|
| 1132 |
+
"registry+https://github.com/rust-lang/crates.io-index#self_cell@1.3.0"
|
| 1133 |
+
]
|
| 1134 |
+
},
|
| 1135 |
+
{
|
| 1136 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-cffi#0.50.0",
|
| 1137 |
+
"dependsOn": [
|
| 1138 |
+
"registry+https://github.com/rust-lang/crates.io-index#cc@1.4.0",
|
| 1139 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 1140 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0",
|
| 1141 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0"
|
| 1142 |
+
]
|
| 1143 |
+
},
|
| 1144 |
+
{
|
| 1145 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-crypto#0.50.0",
|
| 1146 |
+
"dependsOn": [
|
| 1147 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81"
|
| 1148 |
+
]
|
| 1149 |
+
},
|
| 1150 |
+
{
|
| 1151 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-keepalive#0.50.0",
|
| 1152 |
+
"dependsOn": [
|
| 1153 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0"
|
| 1154 |
+
]
|
| 1155 |
+
},
|
| 1156 |
+
{
|
| 1157 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-key-parsing#0.50.0",
|
| 1158 |
+
"dependsOn": [
|
| 1159 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 1160 |
+
"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 1161 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-crypto#0.50.0",
|
| 1162 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-openssl#0.50.0",
|
| 1163 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-x509#0.50.0",
|
| 1164 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 1165 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 1166 |
+
"registry+https://github.com/rust-lang/crates.io-index#pem@4.0.0"
|
| 1167 |
+
]
|
| 1168 |
+
},
|
| 1169 |
+
{
|
| 1170 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-openssl#0.50.0",
|
| 1171 |
+
"dependsOn": [
|
| 1172 |
+
"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 1173 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2",
|
| 1174 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1",
|
| 1175 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 1176 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117"
|
| 1177 |
+
]
|
| 1178 |
+
},
|
| 1179 |
+
{
|
| 1180 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-x509#0.50.0",
|
| 1181 |
+
"dependsOn": [
|
| 1182 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1"
|
| 1183 |
+
]
|
| 1184 |
+
},
|
| 1185 |
+
{
|
| 1186 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-x509-verification#0.50.0",
|
| 1187 |
+
"dependsOn": [
|
| 1188 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 1189 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpWfh3cH/cryptography-50.0.0/src/rust/cryptography-x509#0.50.0"
|
| 1190 |
+
]
|
| 1191 |
+
},
|
| 1192 |
+
{
|
| 1193 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 1194 |
+
"dependsOn": [
|
| 1195 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1",
|
| 1196 |
+
"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18"
|
| 1197 |
+
]
|
| 1198 |
+
},
|
| 1199 |
+
{
|
| 1200 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1",
|
| 1201 |
+
"dependsOn": [
|
| 1202 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107",
|
| 1203 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47",
|
| 1204 |
+
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119"
|
| 1205 |
+
]
|
| 1206 |
+
},
|
| 1207 |
+
{
|
| 1208 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.23.0"
|
| 1209 |
+
},
|
| 1210 |
+
{
|
| 1211 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.1"
|
| 1212 |
+
},
|
| 1213 |
+
{
|
| 1214 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.4.0",
|
| 1215 |
+
"dependsOn": [
|
| 1216 |
+
"registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9",
|
| 1217 |
+
"registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1"
|
| 1218 |
+
]
|
| 1219 |
+
},
|
| 1220 |
+
{
|
| 1221 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4"
|
| 1222 |
+
},
|
| 1223 |
+
{
|
| 1224 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9"
|
| 1225 |
+
},
|
| 1226 |
+
{
|
| 1227 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1"
|
| 1228 |
+
},
|
| 1229 |
+
{
|
| 1230 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2",
|
| 1231 |
+
"dependsOn": [
|
| 1232 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1"
|
| 1233 |
+
]
|
| 1234 |
+
},
|
| 1235 |
+
{
|
| 1236 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0"
|
| 1237 |
+
},
|
| 1238 |
+
{
|
| 1239 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18"
|
| 1240 |
+
},
|
| 1241 |
+
{
|
| 1242 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189"
|
| 1243 |
+
},
|
| 1244 |
+
{
|
| 1245 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4"
|
| 1246 |
+
},
|
| 1247 |
+
{
|
| 1248 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1",
|
| 1249 |
+
"dependsOn": [
|
| 1250 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107",
|
| 1251 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47",
|
| 1252 |
+
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119"
|
| 1253 |
+
]
|
| 1254 |
+
},
|
| 1255 |
+
{
|
| 1256 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 1257 |
+
"dependsOn": [
|
| 1258 |
+
"registry+https://github.com/rust-lang/crates.io-index#cc@1.4.0",
|
| 1259 |
+
"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189",
|
| 1260 |
+
"registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33",
|
| 1261 |
+
"registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15"
|
| 1262 |
+
]
|
| 1263 |
+
},
|
| 1264 |
+
{
|
| 1265 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 1266 |
+
"dependsOn": [
|
| 1267 |
+
"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.1",
|
| 1268 |
+
"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 1269 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2",
|
| 1270 |
+
"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189",
|
| 1271 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1",
|
| 1272 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117"
|
| 1273 |
+
]
|
| 1274 |
+
},
|
| 1275 |
+
{
|
| 1276 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pem@4.0.0",
|
| 1277 |
+
"dependsOn": [
|
| 1278 |
+
"registry+https://github.com/rust-lang/crates.io-index#base64@0.23.0"
|
| 1279 |
+
]
|
| 1280 |
+
},
|
| 1281 |
+
{
|
| 1282 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33"
|
| 1283 |
+
},
|
| 1284 |
+
{
|
| 1285 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.14.0"
|
| 1286 |
+
},
|
| 1287 |
+
{
|
| 1288 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107",
|
| 1289 |
+
"dependsOn": [
|
| 1290 |
+
"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"
|
| 1291 |
+
]
|
| 1292 |
+
},
|
| 1293 |
+
{
|
| 1294 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0",
|
| 1295 |
+
"dependsOn": [
|
| 1296 |
+
"registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5"
|
| 1297 |
+
]
|
| 1298 |
+
},
|
| 1299 |
+
{
|
| 1300 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0",
|
| 1301 |
+
"dependsOn": [
|
| 1302 |
+
"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189",
|
| 1303 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0"
|
| 1304 |
+
]
|
| 1305 |
+
},
|
| 1306 |
+
{
|
| 1307 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0",
|
| 1308 |
+
"dependsOn": [
|
| 1309 |
+
"registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0",
|
| 1310 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107",
|
| 1311 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47",
|
| 1312 |
+
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119"
|
| 1313 |
+
]
|
| 1314 |
+
},
|
| 1315 |
+
{
|
| 1316 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0",
|
| 1317 |
+
"dependsOn": [
|
| 1318 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107",
|
| 1319 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0",
|
| 1320 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47",
|
| 1321 |
+
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119"
|
| 1322 |
+
]
|
| 1323 |
+
},
|
| 1324 |
+
{
|
| 1325 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0",
|
| 1326 |
+
"dependsOn": [
|
| 1327 |
+
"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189",
|
| 1328 |
+
"registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4",
|
| 1329 |
+
"registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.14.0",
|
| 1330 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0",
|
| 1331 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0",
|
| 1332 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0"
|
| 1333 |
+
]
|
| 1334 |
+
},
|
| 1335 |
+
{
|
| 1336 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47",
|
| 1337 |
+
"dependsOn": [
|
| 1338 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107"
|
| 1339 |
+
]
|
| 1340 |
+
},
|
| 1341 |
+
{
|
| 1342 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.3.0"
|
| 1343 |
+
},
|
| 1344 |
+
{
|
| 1345 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1"
|
| 1346 |
+
},
|
| 1347 |
+
{
|
| 1348 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119",
|
| 1349 |
+
"dependsOn": [
|
| 1350 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107",
|
| 1351 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47",
|
| 1352 |
+
"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"
|
| 1353 |
+
]
|
| 1354 |
+
},
|
| 1355 |
+
{
|
| 1356 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5"
|
| 1357 |
+
},
|
| 1358 |
+
{
|
| 1359 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"
|
| 1360 |
+
},
|
| 1361 |
+
{
|
| 1362 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15"
|
| 1363 |
+
}
|
| 1364 |
+
]
|
| 1365 |
+
}
|
.mcp-venv/lib/python3.13/site-packages/cryptography-50.0.0.dist-info/sboms/sbom.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bomFormat": "CycloneDX",
|
| 3 |
+
"specVersion": "1.5",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"serialNumber": "urn:uuid:6120948b-27bc-4bc5-b880-1557c2ab4bd1",
|
| 6 |
+
"metadata": {
|
| 7 |
+
"timestamp": "2026-07-31T11:10:12Z"
|
| 8 |
+
},
|
| 9 |
+
"components": [
|
| 10 |
+
{
|
| 11 |
+
"type": "library",
|
| 12 |
+
"name": "openssl",
|
| 13 |
+
"version": "4.0.1",
|
| 14 |
+
"purl": "pkg:generic/openssl@4.0.1?download_url=https://github.com/openssl/openssl/releases/download/openssl-4.0.1/openssl-4.0.1.tar.gz",
|
| 15 |
+
"hashes": [
|
| 16 |
+
{
|
| 17 |
+
"alg": "SHA-256",
|
| 18 |
+
"content": "2db3f3a0d6ea4b59e1f094ace2c8cd536dffb87cdc39084c5afa1e6f7f37dd09"
|
| 19 |
+
}
|
| 20 |
+
],
|
| 21 |
+
"externalReferences": [
|
| 22 |
+
{
|
| 23 |
+
"type": "distribution",
|
| 24 |
+
"url": "https://github.com/openssl/openssl/releases/download/openssl-4.0.1/openssl-4.0.1.tar.gz"
|
| 25 |
+
}
|
| 26 |
+
],
|
| 27 |
+
"properties": [
|
| 28 |
+
{
|
| 29 |
+
"name": "build:operating-system",
|
| 30 |
+
"value": "linux"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"name": "build:architecture",
|
| 34 |
+
"value": "x86_64"
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"name": "build:flags",
|
| 38 |
+
"value": "no-zlib no-shared no-module no-comp no-apps no-docs no-sm2-precomp no-atexit enable-ec_nistp_64_gcc_128"
|
| 39 |
+
}
|
| 40 |
+
]
|
| 41 |
+
}
|
| 42 |
+
]
|
| 43 |
+
}
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hashes.py
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import abc
|
| 8 |
+
import sys
|
| 9 |
+
|
| 10 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 11 |
+
from cryptography.utils import Buffer
|
| 12 |
+
|
| 13 |
+
__all__ = [
|
| 14 |
+
"MD5",
|
| 15 |
+
"SHA1",
|
| 16 |
+
"SHA3_224",
|
| 17 |
+
"SHA3_256",
|
| 18 |
+
"SHA3_384",
|
| 19 |
+
"SHA3_512",
|
| 20 |
+
"SHA224",
|
| 21 |
+
"SHA256",
|
| 22 |
+
"SHA384",
|
| 23 |
+
"SHA512",
|
| 24 |
+
"SHA512_224",
|
| 25 |
+
"SHA512_256",
|
| 26 |
+
"SHAKE128",
|
| 27 |
+
"SHAKE256",
|
| 28 |
+
"SM3",
|
| 29 |
+
"BLAKE2b",
|
| 30 |
+
"BLAKE2s",
|
| 31 |
+
"ExtendableOutputFunction",
|
| 32 |
+
"Hash",
|
| 33 |
+
"HashAlgorithm",
|
| 34 |
+
"HashContext",
|
| 35 |
+
"XOFHash",
|
| 36 |
+
]
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class HashAlgorithm(metaclass=abc.ABCMeta):
|
| 40 |
+
@property
|
| 41 |
+
@abc.abstractmethod
|
| 42 |
+
def name(self) -> str:
|
| 43 |
+
"""
|
| 44 |
+
A string naming this algorithm (e.g. "sha256", "md5").
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
@property
|
| 48 |
+
@abc.abstractmethod
|
| 49 |
+
def digest_size(self) -> int:
|
| 50 |
+
"""
|
| 51 |
+
The size of the resulting digest in bytes.
|
| 52 |
+
"""
|
| 53 |
+
|
| 54 |
+
@property
|
| 55 |
+
@abc.abstractmethod
|
| 56 |
+
def block_size(self) -> int | None:
|
| 57 |
+
"""
|
| 58 |
+
The internal block size of the hash function, or None if the hash
|
| 59 |
+
function does not use blocks internally (e.g. SHA3).
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class HashContext(metaclass=abc.ABCMeta):
|
| 64 |
+
@property
|
| 65 |
+
@abc.abstractmethod
|
| 66 |
+
def algorithm(self) -> HashAlgorithm:
|
| 67 |
+
"""
|
| 68 |
+
A HashAlgorithm that will be used by this context.
|
| 69 |
+
"""
|
| 70 |
+
|
| 71 |
+
@abc.abstractmethod
|
| 72 |
+
def update(self, data: Buffer) -> None:
|
| 73 |
+
"""
|
| 74 |
+
Processes the provided bytes through the hash.
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
@abc.abstractmethod
|
| 78 |
+
def finalize(self) -> bytes:
|
| 79 |
+
"""
|
| 80 |
+
Finalizes the hash context and returns the hash digest as bytes.
|
| 81 |
+
"""
|
| 82 |
+
|
| 83 |
+
@abc.abstractmethod
|
| 84 |
+
def copy(self) -> HashContext:
|
| 85 |
+
"""
|
| 86 |
+
Return a HashContext that is a copy of the current context.
|
| 87 |
+
"""
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
Hash = rust_openssl.hashes.Hash
|
| 91 |
+
HashContext.register(Hash)
|
| 92 |
+
|
| 93 |
+
XOFHash = rust_openssl.hashes.XOFHash
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
class ExtendableOutputFunction(metaclass=abc.ABCMeta):
|
| 97 |
+
"""
|
| 98 |
+
An interface for extendable output functions.
|
| 99 |
+
"""
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
class SHA1(HashAlgorithm):
|
| 103 |
+
name = "sha1"
|
| 104 |
+
digest_size = 20
|
| 105 |
+
block_size = 64
|
| 106 |
+
|
| 107 |
+
def __eq__(self, other: object) -> bool:
|
| 108 |
+
if not isinstance(other, SHA1):
|
| 109 |
+
return NotImplemented
|
| 110 |
+
|
| 111 |
+
return True
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
class SHA512_224(HashAlgorithm): # noqa: N801
|
| 115 |
+
name = "sha512-224"
|
| 116 |
+
digest_size = 28
|
| 117 |
+
block_size = 128
|
| 118 |
+
|
| 119 |
+
def __eq__(self, other: object) -> bool:
|
| 120 |
+
if not isinstance(other, SHA512_224):
|
| 121 |
+
return NotImplemented
|
| 122 |
+
|
| 123 |
+
return True
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
class SHA512_256(HashAlgorithm): # noqa: N801
|
| 127 |
+
name = "sha512-256"
|
| 128 |
+
digest_size = 32
|
| 129 |
+
block_size = 128
|
| 130 |
+
|
| 131 |
+
def __eq__(self, other: object) -> bool:
|
| 132 |
+
if not isinstance(other, SHA512_256):
|
| 133 |
+
return NotImplemented
|
| 134 |
+
|
| 135 |
+
return True
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class SHA224(HashAlgorithm):
|
| 139 |
+
name = "sha224"
|
| 140 |
+
digest_size = 28
|
| 141 |
+
block_size = 64
|
| 142 |
+
|
| 143 |
+
def __eq__(self, other: object) -> bool:
|
| 144 |
+
if not isinstance(other, SHA224):
|
| 145 |
+
return NotImplemented
|
| 146 |
+
|
| 147 |
+
return True
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
class SHA256(HashAlgorithm):
|
| 151 |
+
name = "sha256"
|
| 152 |
+
digest_size = 32
|
| 153 |
+
block_size = 64
|
| 154 |
+
|
| 155 |
+
def __eq__(self, other: object) -> bool:
|
| 156 |
+
if not isinstance(other, SHA256):
|
| 157 |
+
return NotImplemented
|
| 158 |
+
|
| 159 |
+
return True
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
class SHA384(HashAlgorithm):
|
| 163 |
+
name = "sha384"
|
| 164 |
+
digest_size = 48
|
| 165 |
+
block_size = 128
|
| 166 |
+
|
| 167 |
+
def __eq__(self, other: object) -> bool:
|
| 168 |
+
if not isinstance(other, SHA384):
|
| 169 |
+
return NotImplemented
|
| 170 |
+
|
| 171 |
+
return True
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
class SHA512(HashAlgorithm):
|
| 175 |
+
name = "sha512"
|
| 176 |
+
digest_size = 64
|
| 177 |
+
block_size = 128
|
| 178 |
+
|
| 179 |
+
def __eq__(self, other: object) -> bool:
|
| 180 |
+
if not isinstance(other, SHA512):
|
| 181 |
+
return NotImplemented
|
| 182 |
+
|
| 183 |
+
return True
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
class SHA3_224(HashAlgorithm): # noqa: N801
|
| 187 |
+
name = "sha3-224"
|
| 188 |
+
digest_size = 28
|
| 189 |
+
block_size = None
|
| 190 |
+
|
| 191 |
+
def __eq__(self, other: object) -> bool:
|
| 192 |
+
if not isinstance(other, SHA3_224):
|
| 193 |
+
return NotImplemented
|
| 194 |
+
|
| 195 |
+
return True
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
class SHA3_256(HashAlgorithm): # noqa: N801
|
| 199 |
+
name = "sha3-256"
|
| 200 |
+
digest_size = 32
|
| 201 |
+
block_size = None
|
| 202 |
+
|
| 203 |
+
def __eq__(self, other: object) -> bool:
|
| 204 |
+
if not isinstance(other, SHA3_256):
|
| 205 |
+
return NotImplemented
|
| 206 |
+
|
| 207 |
+
return True
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
class SHA3_384(HashAlgorithm): # noqa: N801
|
| 211 |
+
name = "sha3-384"
|
| 212 |
+
digest_size = 48
|
| 213 |
+
block_size = None
|
| 214 |
+
|
| 215 |
+
def __eq__(self, other: object) -> bool:
|
| 216 |
+
if not isinstance(other, SHA3_384):
|
| 217 |
+
return NotImplemented
|
| 218 |
+
|
| 219 |
+
return True
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
class SHA3_512(HashAlgorithm): # noqa: N801
|
| 223 |
+
name = "sha3-512"
|
| 224 |
+
digest_size = 64
|
| 225 |
+
block_size = None
|
| 226 |
+
|
| 227 |
+
def __eq__(self, other: object) -> bool:
|
| 228 |
+
if not isinstance(other, SHA3_512):
|
| 229 |
+
return NotImplemented
|
| 230 |
+
|
| 231 |
+
return True
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
class SHAKE128(HashAlgorithm, ExtendableOutputFunction):
|
| 235 |
+
name = "shake128"
|
| 236 |
+
block_size = None
|
| 237 |
+
|
| 238 |
+
def __init__(self, digest_size: int):
|
| 239 |
+
if not isinstance(digest_size, int):
|
| 240 |
+
raise TypeError("digest_size must be an integer")
|
| 241 |
+
|
| 242 |
+
if digest_size < 1:
|
| 243 |
+
raise ValueError("digest_size must be a positive integer")
|
| 244 |
+
|
| 245 |
+
self._digest_size = digest_size
|
| 246 |
+
|
| 247 |
+
def __eq__(self, other: object) -> bool:
|
| 248 |
+
if not isinstance(other, SHAKE128):
|
| 249 |
+
return NotImplemented
|
| 250 |
+
|
| 251 |
+
return self._digest_size == other._digest_size
|
| 252 |
+
|
| 253 |
+
@property
|
| 254 |
+
def digest_size(self) -> int:
|
| 255 |
+
return self._digest_size
|
| 256 |
+
|
| 257 |
+
@classmethod
|
| 258 |
+
def xof(cls):
|
| 259 |
+
return cls(sys.maxsize)
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
class SHAKE256(HashAlgorithm, ExtendableOutputFunction):
|
| 263 |
+
name = "shake256"
|
| 264 |
+
block_size = None
|
| 265 |
+
|
| 266 |
+
def __init__(self, digest_size: int):
|
| 267 |
+
if not isinstance(digest_size, int):
|
| 268 |
+
raise TypeError("digest_size must be an integer")
|
| 269 |
+
|
| 270 |
+
if digest_size < 1:
|
| 271 |
+
raise ValueError("digest_size must be a positive integer")
|
| 272 |
+
|
| 273 |
+
self._digest_size = digest_size
|
| 274 |
+
|
| 275 |
+
def __eq__(self, other: object) -> bool:
|
| 276 |
+
if not isinstance(other, SHAKE256):
|
| 277 |
+
return NotImplemented
|
| 278 |
+
|
| 279 |
+
return self._digest_size == other._digest_size
|
| 280 |
+
|
| 281 |
+
@property
|
| 282 |
+
def digest_size(self) -> int:
|
| 283 |
+
return self._digest_size
|
| 284 |
+
|
| 285 |
+
@classmethod
|
| 286 |
+
def xof(cls):
|
| 287 |
+
return cls(sys.maxsize)
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
class MD5(HashAlgorithm):
|
| 291 |
+
name = "md5"
|
| 292 |
+
digest_size = 16
|
| 293 |
+
block_size = 64
|
| 294 |
+
|
| 295 |
+
def __eq__(self, other: object) -> bool:
|
| 296 |
+
if not isinstance(other, MD5):
|
| 297 |
+
return NotImplemented
|
| 298 |
+
|
| 299 |
+
return True
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
class BLAKE2b(HashAlgorithm):
|
| 303 |
+
name = "blake2b"
|
| 304 |
+
_max_digest_size = 64
|
| 305 |
+
_min_digest_size = 1
|
| 306 |
+
block_size = 128
|
| 307 |
+
|
| 308 |
+
def __init__(self, digest_size: int):
|
| 309 |
+
if digest_size != 64:
|
| 310 |
+
raise ValueError("Digest size must be 64")
|
| 311 |
+
|
| 312 |
+
self._digest_size = digest_size
|
| 313 |
+
|
| 314 |
+
def __eq__(self, other: object) -> bool:
|
| 315 |
+
if not isinstance(other, BLAKE2b):
|
| 316 |
+
return NotImplemented
|
| 317 |
+
|
| 318 |
+
return self._digest_size == other._digest_size
|
| 319 |
+
|
| 320 |
+
@property
|
| 321 |
+
def digest_size(self) -> int:
|
| 322 |
+
return self._digest_size
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
class BLAKE2s(HashAlgorithm):
|
| 326 |
+
name = "blake2s"
|
| 327 |
+
block_size = 64
|
| 328 |
+
_max_digest_size = 32
|
| 329 |
+
_min_digest_size = 1
|
| 330 |
+
|
| 331 |
+
def __init__(self, digest_size: int):
|
| 332 |
+
if digest_size != 32:
|
| 333 |
+
raise ValueError("Digest size must be 32")
|
| 334 |
+
|
| 335 |
+
self._digest_size = digest_size
|
| 336 |
+
|
| 337 |
+
def __eq__(self, other: object) -> bool:
|
| 338 |
+
if not isinstance(other, BLAKE2s):
|
| 339 |
+
return NotImplemented
|
| 340 |
+
|
| 341 |
+
return self._digest_size == other._digest_size
|
| 342 |
+
|
| 343 |
+
@property
|
| 344 |
+
def digest_size(self) -> int:
|
| 345 |
+
return self._digest_size
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
class SM3(HashAlgorithm):
|
| 349 |
+
name = "sm3"
|
| 350 |
+
digest_size = 32
|
| 351 |
+
block_size = 64
|
| 352 |
+
|
| 353 |
+
def __eq__(self, other: object) -> bool:
|
| 354 |
+
if not isinstance(other, SM3):
|
| 355 |
+
return NotImplemented
|
| 356 |
+
|
| 357 |
+
return True
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hmac.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
from cryptography.hazmat.primitives import hashes
|
| 9 |
+
|
| 10 |
+
__all__ = ["HMAC"]
|
| 11 |
+
|
| 12 |
+
HMAC = rust_openssl.hmac.HMAC
|
| 13 |
+
hashes.HashContext.register(HMAC)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hpke.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
|
| 9 |
+
AEAD = rust_openssl.hpke.AEAD
|
| 10 |
+
KDF = rust_openssl.hpke.KDF
|
| 11 |
+
KEM = rust_openssl.hpke.KEM
|
| 12 |
+
MLKEM768X25519PrivateKey = rust_openssl.hpke.MLKEM768X25519PrivateKey
|
| 13 |
+
MLKEM768X25519PublicKey = rust_openssl.hpke.MLKEM768X25519PublicKey
|
| 14 |
+
MLKEM1024P384PrivateKey = rust_openssl.hpke.MLKEM1024P384PrivateKey
|
| 15 |
+
MLKEM1024P384PublicKey = rust_openssl.hpke.MLKEM1024P384PublicKey
|
| 16 |
+
Suite = rust_openssl.hpke.Suite
|
| 17 |
+
|
| 18 |
+
__all__ = [
|
| 19 |
+
"AEAD",
|
| 20 |
+
"KDF",
|
| 21 |
+
"KEM",
|
| 22 |
+
"MLKEM768X25519PrivateKey",
|
| 23 |
+
"MLKEM768X25519PublicKey",
|
| 24 |
+
"MLKEM1024P384PrivateKey",
|
| 25 |
+
"MLKEM1024P384PublicKey",
|
| 26 |
+
"Suite",
|
| 27 |
+
]
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/__init__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import abc
|
| 8 |
+
|
| 9 |
+
from cryptography.utils import Buffer
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class KeyDerivationFunction(metaclass=abc.ABCMeta):
|
| 13 |
+
@abc.abstractmethod
|
| 14 |
+
def derive(self, key_material: bytes) -> bytes:
|
| 15 |
+
"""
|
| 16 |
+
Deterministically generates and returns a new key based on the existing
|
| 17 |
+
key material.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
@abc.abstractmethod
|
| 21 |
+
def derive_into(self, key_material: bytes, buffer: Buffer) -> None:
|
| 22 |
+
"""
|
| 23 |
+
Deterministically generates a new key based on the existing key
|
| 24 |
+
material and stores it in the provided buffer.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
@abc.abstractmethod
|
| 28 |
+
def verify(self, key_material: bytes, expected_key: bytes) -> None:
|
| 29 |
+
"""
|
| 30 |
+
Checks whether the key generated by the key material matches the
|
| 31 |
+
expected derived key. Raises an exception if they do not match.
|
| 32 |
+
"""
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/argon2.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
|
| 9 |
+
|
| 10 |
+
Argon2d = rust_openssl.kdf.Argon2d
|
| 11 |
+
Argon2i = rust_openssl.kdf.Argon2i
|
| 12 |
+
Argon2id = rust_openssl.kdf.Argon2id
|
| 13 |
+
KeyDerivationFunction.register(Argon2d)
|
| 14 |
+
KeyDerivationFunction.register(Argon2i)
|
| 15 |
+
KeyDerivationFunction.register(Argon2id)
|
| 16 |
+
|
| 17 |
+
__all__ = ["Argon2d", "Argon2i", "Argon2id"]
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
|
| 9 |
+
|
| 10 |
+
ConcatKDFHash = rust_openssl.kdf.ConcatKDFHash
|
| 11 |
+
ConcatKDFHMAC = rust_openssl.kdf.ConcatKDFHMAC
|
| 12 |
+
|
| 13 |
+
KeyDerivationFunction.register(ConcatKDFHash)
|
| 14 |
+
KeyDerivationFunction.register(ConcatKDFHMAC)
|
| 15 |
+
|
| 16 |
+
__all__ = ["ConcatKDFHMAC", "ConcatKDFHash"]
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
|
| 9 |
+
|
| 10 |
+
HKDF = rust_openssl.kdf.HKDF
|
| 11 |
+
HKDFExpand = rust_openssl.kdf.HKDFExpand
|
| 12 |
+
|
| 13 |
+
KeyDerivationFunction.register(HKDF)
|
| 14 |
+
KeyDerivationFunction.register(HKDFExpand)
|
| 15 |
+
|
| 16 |
+
__all__ = ["HKDF", "HKDFExpand"]
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography import utils
|
| 8 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 9 |
+
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class Mode(utils.Enum):
|
| 13 |
+
CounterMode = "ctr"
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class CounterLocation(utils.Enum):
|
| 17 |
+
BeforeFixed = "before_fixed"
|
| 18 |
+
AfterFixed = "after_fixed"
|
| 19 |
+
MiddleFixed = "middle_fixed"
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
KBKDFHMAC = rust_openssl.kdf.KBKDFHMAC
|
| 23 |
+
KeyDerivationFunction.register(KBKDFHMAC)
|
| 24 |
+
|
| 25 |
+
KBKDFCMAC = rust_openssl.kdf.KBKDFCMAC
|
| 26 |
+
KeyDerivationFunction.register(KBKDFCMAC)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
|
| 9 |
+
|
| 10 |
+
PBKDF2HMAC = rust_openssl.kdf.PBKDF2HMAC
|
| 11 |
+
KeyDerivationFunction.register(PBKDF2HMAC)
|
| 12 |
+
|
| 13 |
+
__all__ = ["PBKDF2HMAC"]
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import sys
|
| 8 |
+
|
| 9 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 10 |
+
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
|
| 11 |
+
|
| 12 |
+
# This is used by the scrypt tests to skip tests that require more memory
|
| 13 |
+
# than the MEM_LIMIT
|
| 14 |
+
_MEM_LIMIT = sys.maxsize // 2
|
| 15 |
+
|
| 16 |
+
Scrypt = rust_openssl.kdf.Scrypt
|
| 17 |
+
KeyDerivationFunction.register(Scrypt)
|
| 18 |
+
|
| 19 |
+
__all__ = ["Scrypt"]
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
|
| 9 |
+
|
| 10 |
+
X963KDF = rust_openssl.kdf.X963KDF
|
| 11 |
+
KeyDerivationFunction.register(X963KDF)
|
| 12 |
+
|
| 13 |
+
__all__ = ["X963KDF"]
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/keywrap.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
|
| 9 |
+
__all__ = [
|
| 10 |
+
"InvalidUnwrap",
|
| 11 |
+
"aes_key_unwrap",
|
| 12 |
+
"aes_key_unwrap_with_padding",
|
| 13 |
+
"aes_key_wrap",
|
| 14 |
+
"aes_key_wrap_with_padding",
|
| 15 |
+
]
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class InvalidUnwrap(Exception):
|
| 19 |
+
pass
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
aes_key_wrap = rust_openssl.keywrap.aes_key_wrap
|
| 23 |
+
aes_key_unwrap = rust_openssl.keywrap.aes_key_unwrap
|
| 24 |
+
aes_key_wrap_with_padding = rust_openssl.keywrap.aes_key_wrap_with_padding
|
| 25 |
+
aes_key_unwrap_with_padding = rust_openssl.keywrap.aes_key_unwrap_with_padding
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/padding.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import abc
|
| 8 |
+
|
| 9 |
+
from cryptography import utils
|
| 10 |
+
from cryptography.hazmat.bindings._rust import (
|
| 11 |
+
ANSIX923PaddingContext,
|
| 12 |
+
ANSIX923UnpaddingContext,
|
| 13 |
+
PKCS7PaddingContext,
|
| 14 |
+
PKCS7UnpaddingContext,
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class PaddingContext(metaclass=abc.ABCMeta):
|
| 19 |
+
@abc.abstractmethod
|
| 20 |
+
def update(self, data: utils.Buffer) -> bytes:
|
| 21 |
+
"""
|
| 22 |
+
Pads the provided bytes and returns any available data as bytes.
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
@abc.abstractmethod
|
| 26 |
+
def finalize(self) -> bytes:
|
| 27 |
+
"""
|
| 28 |
+
Finalize the padding, returns bytes.
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _byte_padding_check(block_size: int) -> None:
|
| 33 |
+
if not (0 <= block_size <= 2040):
|
| 34 |
+
raise ValueError("block_size must be in range(0, 2041).")
|
| 35 |
+
|
| 36 |
+
if block_size % 8 != 0:
|
| 37 |
+
raise ValueError("block_size must be a multiple of 8.")
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class PKCS7:
|
| 41 |
+
def __init__(self, block_size: int):
|
| 42 |
+
_byte_padding_check(block_size)
|
| 43 |
+
self.block_size = block_size
|
| 44 |
+
|
| 45 |
+
def padder(self) -> PaddingContext:
|
| 46 |
+
return PKCS7PaddingContext(self.block_size)
|
| 47 |
+
|
| 48 |
+
def unpadder(self) -> PaddingContext:
|
| 49 |
+
return PKCS7UnpaddingContext(self.block_size)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
PaddingContext.register(PKCS7PaddingContext)
|
| 53 |
+
PaddingContext.register(PKCS7UnpaddingContext)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class ANSIX923:
|
| 57 |
+
def __init__(self, block_size: int):
|
| 58 |
+
_byte_padding_check(block_size)
|
| 59 |
+
self.block_size = block_size
|
| 60 |
+
|
| 61 |
+
def padder(self) -> PaddingContext:
|
| 62 |
+
return ANSIX923PaddingContext(self.block_size)
|
| 63 |
+
|
| 64 |
+
def unpadder(self) -> PaddingContext:
|
| 65 |
+
return ANSIX923UnpaddingContext(self.block_size)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
PaddingContext.register(ANSIX923PaddingContext)
|
| 69 |
+
PaddingContext.register(ANSIX923UnpaddingContext)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/poly1305.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
|
| 9 |
+
__all__ = ["Poly1305"]
|
| 10 |
+
|
| 11 |
+
Poly1305 = rust_openssl.poly1305.Poly1305
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/__init__.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography import utils
|
| 8 |
+
from cryptography.hazmat.primitives._serialization import (
|
| 9 |
+
BestAvailableEncryption,
|
| 10 |
+
Encoding,
|
| 11 |
+
KeySerializationEncryption,
|
| 12 |
+
NoEncryption,
|
| 13 |
+
ParameterFormat,
|
| 14 |
+
PrivateFormat,
|
| 15 |
+
PublicFormat,
|
| 16 |
+
_KeySerializationEncryption,
|
| 17 |
+
)
|
| 18 |
+
from cryptography.hazmat.primitives.asymmetric.dh import _FFDH_DEPRECATION_MSG
|
| 19 |
+
from cryptography.hazmat.primitives.serialization.base import (
|
| 20 |
+
load_der_parameters,
|
| 21 |
+
load_der_private_key,
|
| 22 |
+
load_der_public_key,
|
| 23 |
+
load_pem_parameters,
|
| 24 |
+
load_pem_private_key,
|
| 25 |
+
load_pem_public_key,
|
| 26 |
+
)
|
| 27 |
+
from cryptography.hazmat.primitives.serialization.ssh import (
|
| 28 |
+
SSHCertificate,
|
| 29 |
+
SSHCertificateBuilder,
|
| 30 |
+
SSHCertificateType,
|
| 31 |
+
SSHCertPrivateKeyTypes,
|
| 32 |
+
SSHCertPublicKeyTypes,
|
| 33 |
+
SSHPrivateKeyTypes,
|
| 34 |
+
SSHPublicKeyTypes,
|
| 35 |
+
load_ssh_private_key,
|
| 36 |
+
load_ssh_public_identity,
|
| 37 |
+
load_ssh_public_key,
|
| 38 |
+
ssh_key_fingerprint,
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
__all__ = [
|
| 42 |
+
"BestAvailableEncryption",
|
| 43 |
+
"Encoding",
|
| 44 |
+
"KeySerializationEncryption",
|
| 45 |
+
"NoEncryption",
|
| 46 |
+
"ParameterFormat",
|
| 47 |
+
"PrivateFormat",
|
| 48 |
+
"PublicFormat",
|
| 49 |
+
"SSHCertPrivateKeyTypes",
|
| 50 |
+
"SSHCertPublicKeyTypes",
|
| 51 |
+
"SSHCertificate",
|
| 52 |
+
"SSHCertificateBuilder",
|
| 53 |
+
"SSHCertificateType",
|
| 54 |
+
"SSHPrivateKeyTypes",
|
| 55 |
+
"SSHPublicKeyTypes",
|
| 56 |
+
"_KeySerializationEncryption",
|
| 57 |
+
"load_der_parameters",
|
| 58 |
+
"load_der_private_key",
|
| 59 |
+
"load_der_public_key",
|
| 60 |
+
"load_pem_parameters",
|
| 61 |
+
"load_pem_private_key",
|
| 62 |
+
"load_pem_public_key",
|
| 63 |
+
"load_ssh_private_key",
|
| 64 |
+
"load_ssh_public_identity",
|
| 65 |
+
"load_ssh_public_key",
|
| 66 |
+
"ssh_key_fingerprint",
|
| 67 |
+
]
|
| 68 |
+
|
| 69 |
+
# These can only load FFDH parameters, so the functions themselves are
|
| 70 |
+
# deprecated alongside the rest of FFDH.
|
| 71 |
+
utils.deprecated(
|
| 72 |
+
load_pem_parameters,
|
| 73 |
+
__name__,
|
| 74 |
+
_FFDH_DEPRECATION_MSG,
|
| 75 |
+
utils.DeprecatedIn50,
|
| 76 |
+
name="load_pem_parameters",
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
utils.deprecated(
|
| 80 |
+
load_der_parameters,
|
| 81 |
+
__name__,
|
| 82 |
+
_FFDH_DEPRECATION_MSG,
|
| 83 |
+
utils.DeprecatedIn50,
|
| 84 |
+
name="load_der_parameters",
|
| 85 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/base.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 8 |
+
|
| 9 |
+
load_pem_private_key = rust_openssl.keys.load_pem_private_key
|
| 10 |
+
load_der_private_key = rust_openssl.keys.load_der_private_key
|
| 11 |
+
|
| 12 |
+
load_pem_public_key = rust_openssl.keys.load_pem_public_key
|
| 13 |
+
load_der_public_key = rust_openssl.keys.load_der_public_key
|
| 14 |
+
|
| 15 |
+
load_pem_parameters = rust_openssl.dh.from_pem_parameters
|
| 16 |
+
load_der_parameters = rust_openssl.dh.from_der_parameters
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import typing
|
| 8 |
+
from collections.abc import Iterable
|
| 9 |
+
|
| 10 |
+
from cryptography import x509
|
| 11 |
+
from cryptography.hazmat.bindings._rust import pkcs12 as rust_pkcs12
|
| 12 |
+
from cryptography.hazmat.primitives import serialization
|
| 13 |
+
from cryptography.hazmat.primitives._serialization import PBES as PBES
|
| 14 |
+
from cryptography.hazmat.primitives.asymmetric import (
|
| 15 |
+
dsa,
|
| 16 |
+
ec,
|
| 17 |
+
ed448,
|
| 18 |
+
ed25519,
|
| 19 |
+
rsa,
|
| 20 |
+
)
|
| 21 |
+
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
|
| 22 |
+
|
| 23 |
+
__all__ = [
|
| 24 |
+
"PBES",
|
| 25 |
+
"PKCS12Certificate",
|
| 26 |
+
"PKCS12KeyAndCertificates",
|
| 27 |
+
"PKCS12PrivateKeyTypes",
|
| 28 |
+
"load_key_and_certificates",
|
| 29 |
+
"load_pkcs12",
|
| 30 |
+
"serialize_java_truststore",
|
| 31 |
+
"serialize_key_and_certificates",
|
| 32 |
+
]
|
| 33 |
+
|
| 34 |
+
PKCS12PrivateKeyTypes = typing.Union[
|
| 35 |
+
rsa.RSAPrivateKey,
|
| 36 |
+
dsa.DSAPrivateKey,
|
| 37 |
+
ec.EllipticCurvePrivateKey,
|
| 38 |
+
ed25519.Ed25519PrivateKey,
|
| 39 |
+
ed448.Ed448PrivateKey,
|
| 40 |
+
]
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
PKCS12Certificate = rust_pkcs12.PKCS12Certificate
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class PKCS12KeyAndCertificates:
|
| 47 |
+
def __init__(
|
| 48 |
+
self,
|
| 49 |
+
key: PrivateKeyTypes | None,
|
| 50 |
+
cert: PKCS12Certificate | None,
|
| 51 |
+
additional_certs: list[PKCS12Certificate],
|
| 52 |
+
):
|
| 53 |
+
if key is not None and not isinstance(
|
| 54 |
+
key,
|
| 55 |
+
(
|
| 56 |
+
rsa.RSAPrivateKey,
|
| 57 |
+
dsa.DSAPrivateKey,
|
| 58 |
+
ec.EllipticCurvePrivateKey,
|
| 59 |
+
ed25519.Ed25519PrivateKey,
|
| 60 |
+
ed448.Ed448PrivateKey,
|
| 61 |
+
),
|
| 62 |
+
):
|
| 63 |
+
raise TypeError(
|
| 64 |
+
"Key must be RSA, DSA, EllipticCurve, ED25519, or ED448"
|
| 65 |
+
" private key, or None."
|
| 66 |
+
)
|
| 67 |
+
if cert is not None and not isinstance(cert, PKCS12Certificate):
|
| 68 |
+
raise TypeError("cert must be a PKCS12Certificate object or None")
|
| 69 |
+
if not all(
|
| 70 |
+
isinstance(add_cert, PKCS12Certificate)
|
| 71 |
+
for add_cert in additional_certs
|
| 72 |
+
):
|
| 73 |
+
raise TypeError(
|
| 74 |
+
"all values in additional_certs must be PKCS12Certificate"
|
| 75 |
+
" objects"
|
| 76 |
+
)
|
| 77 |
+
self._key = key
|
| 78 |
+
self._cert = cert
|
| 79 |
+
self._additional_certs = additional_certs
|
| 80 |
+
|
| 81 |
+
@property
|
| 82 |
+
def key(self) -> PrivateKeyTypes | None:
|
| 83 |
+
return self._key
|
| 84 |
+
|
| 85 |
+
@property
|
| 86 |
+
def cert(self) -> PKCS12Certificate | None:
|
| 87 |
+
return self._cert
|
| 88 |
+
|
| 89 |
+
@property
|
| 90 |
+
def additional_certs(self) -> list[PKCS12Certificate]:
|
| 91 |
+
return self._additional_certs
|
| 92 |
+
|
| 93 |
+
def __eq__(self, other: object) -> bool:
|
| 94 |
+
if not isinstance(other, PKCS12KeyAndCertificates):
|
| 95 |
+
return NotImplemented
|
| 96 |
+
|
| 97 |
+
return (
|
| 98 |
+
self.key == other.key
|
| 99 |
+
and self.cert == other.cert
|
| 100 |
+
and self.additional_certs == other.additional_certs
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
def __hash__(self) -> int:
|
| 104 |
+
return hash((self.key, self.cert, tuple(self.additional_certs)))
|
| 105 |
+
|
| 106 |
+
def __repr__(self) -> str:
|
| 107 |
+
fmt = (
|
| 108 |
+
"<PKCS12KeyAndCertificates(key={}, cert={}, additional_certs={})>"
|
| 109 |
+
)
|
| 110 |
+
return fmt.format(self.key, self.cert, self.additional_certs)
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
load_key_and_certificates = rust_pkcs12.load_key_and_certificates
|
| 114 |
+
load_pkcs12 = rust_pkcs12.load_pkcs12
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
_PKCS12CATypes = typing.Union[
|
| 118 |
+
x509.Certificate,
|
| 119 |
+
PKCS12Certificate,
|
| 120 |
+
]
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def serialize_java_truststore(
|
| 124 |
+
certs: Iterable[PKCS12Certificate],
|
| 125 |
+
encryption_algorithm: serialization.KeySerializationEncryption,
|
| 126 |
+
) -> bytes:
|
| 127 |
+
if not certs:
|
| 128 |
+
raise ValueError("You must supply at least one cert")
|
| 129 |
+
|
| 130 |
+
if not isinstance(
|
| 131 |
+
encryption_algorithm, serialization.KeySerializationEncryption
|
| 132 |
+
):
|
| 133 |
+
raise TypeError(
|
| 134 |
+
"Key encryption algorithm must be a "
|
| 135 |
+
"KeySerializationEncryption instance"
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
return rust_pkcs12.serialize_java_truststore(certs, encryption_algorithm)
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def serialize_key_and_certificates(
|
| 142 |
+
name: bytes | None,
|
| 143 |
+
key: PKCS12PrivateKeyTypes | None,
|
| 144 |
+
cert: x509.Certificate | None,
|
| 145 |
+
cas: Iterable[_PKCS12CATypes] | None,
|
| 146 |
+
encryption_algorithm: serialization.KeySerializationEncryption,
|
| 147 |
+
) -> bytes:
|
| 148 |
+
if key is not None and not isinstance(
|
| 149 |
+
key,
|
| 150 |
+
(
|
| 151 |
+
rsa.RSAPrivateKey,
|
| 152 |
+
dsa.DSAPrivateKey,
|
| 153 |
+
ec.EllipticCurvePrivateKey,
|
| 154 |
+
ed25519.Ed25519PrivateKey,
|
| 155 |
+
ed448.Ed448PrivateKey,
|
| 156 |
+
),
|
| 157 |
+
):
|
| 158 |
+
raise TypeError(
|
| 159 |
+
"Key must be RSA, DSA, EllipticCurve, ED25519, or ED448"
|
| 160 |
+
" private key, or None."
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
if not isinstance(
|
| 164 |
+
encryption_algorithm, serialization.KeySerializationEncryption
|
| 165 |
+
):
|
| 166 |
+
raise TypeError(
|
| 167 |
+
"Key encryption algorithm must be a "
|
| 168 |
+
"KeySerializationEncryption instance"
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
if key is None and cert is None and not cas:
|
| 172 |
+
raise ValueError("You must supply at least one of key, cert, or cas")
|
| 173 |
+
|
| 174 |
+
return rust_pkcs12.serialize_key_and_certificates(
|
| 175 |
+
name, key, cert, cas, encryption_algorithm
|
| 176 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import email.base64mime
|
| 8 |
+
import email.generator
|
| 9 |
+
import email.message
|
| 10 |
+
import email.policy
|
| 11 |
+
import io
|
| 12 |
+
import typing
|
| 13 |
+
from collections.abc import Iterable
|
| 14 |
+
|
| 15 |
+
from cryptography import utils, x509
|
| 16 |
+
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
|
| 17 |
+
from cryptography.hazmat.bindings._rust import pkcs7 as rust_pkcs7
|
| 18 |
+
from cryptography.hazmat.primitives import hashes, serialization
|
| 19 |
+
from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa
|
| 20 |
+
from cryptography.hazmat.primitives.ciphers import (
|
| 21 |
+
algorithms,
|
| 22 |
+
)
|
| 23 |
+
from cryptography.utils import _check_byteslike
|
| 24 |
+
|
| 25 |
+
load_pem_pkcs7_certificates = rust_pkcs7.load_pem_pkcs7_certificates
|
| 26 |
+
|
| 27 |
+
load_der_pkcs7_certificates = rust_pkcs7.load_der_pkcs7_certificates
|
| 28 |
+
|
| 29 |
+
serialize_certificates = rust_pkcs7.serialize_certificates
|
| 30 |
+
|
| 31 |
+
PKCS7HashTypes = typing.Union[
|
| 32 |
+
hashes.SHA224,
|
| 33 |
+
hashes.SHA256,
|
| 34 |
+
hashes.SHA384,
|
| 35 |
+
hashes.SHA512,
|
| 36 |
+
]
|
| 37 |
+
|
| 38 |
+
PKCS7PrivateKeyTypes = typing.Union[
|
| 39 |
+
rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey
|
| 40 |
+
]
|
| 41 |
+
|
| 42 |
+
ContentEncryptionAlgorithm = typing.Union[
|
| 43 |
+
type[algorithms.AES128], type[algorithms.AES256]
|
| 44 |
+
]
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class PKCS7Options(utils.Enum):
|
| 48 |
+
Text = "Add text/plain MIME type"
|
| 49 |
+
Binary = "Don't translate input data into canonical MIME format"
|
| 50 |
+
DetachedSignature = "Don't embed data in the PKCS7 structure"
|
| 51 |
+
NoCapabilities = "Don't embed SMIME capabilities"
|
| 52 |
+
NoAttributes = "Don't embed authenticatedAttributes"
|
| 53 |
+
NoCerts = "Don't embed signer certificate"
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class PKCS7SignatureBuilder:
|
| 57 |
+
def __init__(
|
| 58 |
+
self,
|
| 59 |
+
data: utils.Buffer | None = None,
|
| 60 |
+
signers: list[
|
| 61 |
+
tuple[
|
| 62 |
+
x509.Certificate,
|
| 63 |
+
PKCS7PrivateKeyTypes,
|
| 64 |
+
PKCS7HashTypes,
|
| 65 |
+
padding.PSS | padding.PKCS1v15 | None,
|
| 66 |
+
]
|
| 67 |
+
] = [],
|
| 68 |
+
additional_certs: list[x509.Certificate] = [],
|
| 69 |
+
):
|
| 70 |
+
self._data = data
|
| 71 |
+
self._signers = signers
|
| 72 |
+
self._additional_certs = additional_certs
|
| 73 |
+
|
| 74 |
+
def set_data(self, data: utils.Buffer) -> PKCS7SignatureBuilder:
|
| 75 |
+
_check_byteslike("data", data)
|
| 76 |
+
if self._data is not None:
|
| 77 |
+
raise ValueError("data may only be set once")
|
| 78 |
+
|
| 79 |
+
return PKCS7SignatureBuilder(data, self._signers)
|
| 80 |
+
|
| 81 |
+
def add_signer(
|
| 82 |
+
self,
|
| 83 |
+
certificate: x509.Certificate,
|
| 84 |
+
private_key: PKCS7PrivateKeyTypes,
|
| 85 |
+
hash_algorithm: PKCS7HashTypes,
|
| 86 |
+
*,
|
| 87 |
+
rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
|
| 88 |
+
) -> PKCS7SignatureBuilder:
|
| 89 |
+
if not isinstance(
|
| 90 |
+
hash_algorithm,
|
| 91 |
+
(
|
| 92 |
+
hashes.SHA224,
|
| 93 |
+
hashes.SHA256,
|
| 94 |
+
hashes.SHA384,
|
| 95 |
+
hashes.SHA512,
|
| 96 |
+
),
|
| 97 |
+
):
|
| 98 |
+
raise TypeError(
|
| 99 |
+
"hash_algorithm must be one of hashes.SHA224, "
|
| 100 |
+
"SHA256, SHA384, or SHA512"
|
| 101 |
+
)
|
| 102 |
+
if not isinstance(certificate, x509.Certificate):
|
| 103 |
+
raise TypeError("certificate must be a x509.Certificate")
|
| 104 |
+
|
| 105 |
+
if not isinstance(
|
| 106 |
+
private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)
|
| 107 |
+
):
|
| 108 |
+
raise TypeError("Only RSA & EC keys are supported at this time.")
|
| 109 |
+
|
| 110 |
+
if rsa_padding is not None:
|
| 111 |
+
if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
|
| 112 |
+
raise TypeError("Padding must be PSS or PKCS1v15")
|
| 113 |
+
if not isinstance(private_key, rsa.RSAPrivateKey):
|
| 114 |
+
raise TypeError("Padding is only supported for RSA keys")
|
| 115 |
+
|
| 116 |
+
return PKCS7SignatureBuilder(
|
| 117 |
+
self._data,
|
| 118 |
+
[
|
| 119 |
+
*self._signers,
|
| 120 |
+
(certificate, private_key, hash_algorithm, rsa_padding),
|
| 121 |
+
],
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
def add_certificate(
|
| 125 |
+
self, certificate: x509.Certificate
|
| 126 |
+
) -> PKCS7SignatureBuilder:
|
| 127 |
+
if not isinstance(certificate, x509.Certificate):
|
| 128 |
+
raise TypeError("certificate must be a x509.Certificate")
|
| 129 |
+
|
| 130 |
+
return PKCS7SignatureBuilder(
|
| 131 |
+
self._data, self._signers, [*self._additional_certs, certificate]
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
def sign(
|
| 135 |
+
self,
|
| 136 |
+
encoding: serialization.Encoding,
|
| 137 |
+
options: Iterable[PKCS7Options],
|
| 138 |
+
backend: typing.Any = None,
|
| 139 |
+
) -> bytes:
|
| 140 |
+
if len(self._signers) == 0:
|
| 141 |
+
raise ValueError("Must have at least one signer")
|
| 142 |
+
if self._data is None:
|
| 143 |
+
raise ValueError("You must add data to sign")
|
| 144 |
+
options = list(options)
|
| 145 |
+
if not all(isinstance(x, PKCS7Options) for x in options):
|
| 146 |
+
raise ValueError("options must be from the PKCS7Options enum")
|
| 147 |
+
if encoding not in (
|
| 148 |
+
serialization.Encoding.PEM,
|
| 149 |
+
serialization.Encoding.DER,
|
| 150 |
+
serialization.Encoding.SMIME,
|
| 151 |
+
):
|
| 152 |
+
raise ValueError(
|
| 153 |
+
"Must be PEM, DER, or SMIME from the Encoding enum"
|
| 154 |
+
)
|
| 155 |
+
|
| 156 |
+
# Text is a meaningless option unless it is accompanied by
|
| 157 |
+
# DetachedSignature
|
| 158 |
+
if (
|
| 159 |
+
PKCS7Options.Text in options
|
| 160 |
+
and PKCS7Options.DetachedSignature not in options
|
| 161 |
+
):
|
| 162 |
+
raise ValueError(
|
| 163 |
+
"When passing the Text option you must also pass "
|
| 164 |
+
"DetachedSignature"
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
if PKCS7Options.Text in options and encoding in (
|
| 168 |
+
serialization.Encoding.DER,
|
| 169 |
+
serialization.Encoding.PEM,
|
| 170 |
+
):
|
| 171 |
+
raise ValueError(
|
| 172 |
+
"The Text option is only available for SMIME serialization"
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
# No attributes implies no capabilities so we'll error if you try to
|
| 176 |
+
# pass both.
|
| 177 |
+
if (
|
| 178 |
+
PKCS7Options.NoAttributes in options
|
| 179 |
+
and PKCS7Options.NoCapabilities in options
|
| 180 |
+
):
|
| 181 |
+
raise ValueError(
|
| 182 |
+
"NoAttributes is a superset of NoCapabilities. Do not pass "
|
| 183 |
+
"both values."
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
return rust_pkcs7.sign_and_serialize(self, encoding, options)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
class PKCS7EnvelopeBuilder:
|
| 190 |
+
def __init__(
|
| 191 |
+
self,
|
| 192 |
+
*,
|
| 193 |
+
_data: bytes | None = None,
|
| 194 |
+
_recipients: list[x509.Certificate] | None = None,
|
| 195 |
+
_content_encryption_algorithm: ContentEncryptionAlgorithm
|
| 196 |
+
| None = None,
|
| 197 |
+
):
|
| 198 |
+
from cryptography.hazmat.backends.openssl.backend import (
|
| 199 |
+
backend as ossl,
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
if not ossl.rsa_encryption_supported(padding=padding.PKCS1v15()):
|
| 203 |
+
raise UnsupportedAlgorithm(
|
| 204 |
+
"RSA with PKCS1 v1.5 padding is not supported by this version"
|
| 205 |
+
" of OpenSSL.",
|
| 206 |
+
_Reasons.UNSUPPORTED_PADDING,
|
| 207 |
+
)
|
| 208 |
+
self._data = _data
|
| 209 |
+
self._recipients = _recipients if _recipients is not None else []
|
| 210 |
+
self._content_encryption_algorithm = _content_encryption_algorithm
|
| 211 |
+
|
| 212 |
+
def set_data(self, data: bytes) -> PKCS7EnvelopeBuilder:
|
| 213 |
+
_check_byteslike("data", data)
|
| 214 |
+
if self._data is not None:
|
| 215 |
+
raise ValueError("data may only be set once")
|
| 216 |
+
|
| 217 |
+
return PKCS7EnvelopeBuilder(
|
| 218 |
+
_data=data,
|
| 219 |
+
_recipients=self._recipients,
|
| 220 |
+
_content_encryption_algorithm=self._content_encryption_algorithm,
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
def add_recipient(
|
| 224 |
+
self,
|
| 225 |
+
certificate: x509.Certificate,
|
| 226 |
+
) -> PKCS7EnvelopeBuilder:
|
| 227 |
+
if not isinstance(certificate, x509.Certificate):
|
| 228 |
+
raise TypeError("certificate must be a x509.Certificate")
|
| 229 |
+
|
| 230 |
+
if not isinstance(certificate.public_key(), rsa.RSAPublicKey):
|
| 231 |
+
raise TypeError("Only RSA keys are supported at this time.")
|
| 232 |
+
|
| 233 |
+
return PKCS7EnvelopeBuilder(
|
| 234 |
+
_data=self._data,
|
| 235 |
+
_recipients=[
|
| 236 |
+
*self._recipients,
|
| 237 |
+
certificate,
|
| 238 |
+
],
|
| 239 |
+
_content_encryption_algorithm=self._content_encryption_algorithm,
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
def set_content_encryption_algorithm(
|
| 243 |
+
self, content_encryption_algorithm: ContentEncryptionAlgorithm
|
| 244 |
+
) -> PKCS7EnvelopeBuilder:
|
| 245 |
+
if self._content_encryption_algorithm is not None:
|
| 246 |
+
raise ValueError("Content encryption algo may only be set once")
|
| 247 |
+
if content_encryption_algorithm not in {
|
| 248 |
+
algorithms.AES128,
|
| 249 |
+
algorithms.AES256,
|
| 250 |
+
}:
|
| 251 |
+
raise TypeError("Only AES128 and AES256 are supported")
|
| 252 |
+
|
| 253 |
+
return PKCS7EnvelopeBuilder(
|
| 254 |
+
_data=self._data,
|
| 255 |
+
_recipients=self._recipients,
|
| 256 |
+
_content_encryption_algorithm=content_encryption_algorithm,
|
| 257 |
+
)
|
| 258 |
+
|
| 259 |
+
def encrypt(
|
| 260 |
+
self,
|
| 261 |
+
encoding: serialization.Encoding,
|
| 262 |
+
options: Iterable[PKCS7Options],
|
| 263 |
+
) -> bytes:
|
| 264 |
+
if len(self._recipients) == 0:
|
| 265 |
+
raise ValueError("Must have at least one recipient")
|
| 266 |
+
if self._data is None:
|
| 267 |
+
raise ValueError("You must add data to encrypt")
|
| 268 |
+
|
| 269 |
+
# The default content encryption algorithm is AES-128-CBC, which the
|
| 270 |
+
# S/MIME v3.2 RFC specifies as MUST support (https://datatracker.ietf.org/doc/html/rfc5751#section-2.7)
|
| 271 |
+
# however rest of S/MIME v3.2 is not currently supported
|
| 272 |
+
content_encryption_algorithm = (
|
| 273 |
+
self._content_encryption_algorithm or algorithms.AES128
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
options = list(options)
|
| 277 |
+
if not all(isinstance(x, PKCS7Options) for x in options):
|
| 278 |
+
raise ValueError("options must be from the PKCS7Options enum")
|
| 279 |
+
if encoding not in (
|
| 280 |
+
serialization.Encoding.PEM,
|
| 281 |
+
serialization.Encoding.DER,
|
| 282 |
+
serialization.Encoding.SMIME,
|
| 283 |
+
):
|
| 284 |
+
raise ValueError(
|
| 285 |
+
"Must be PEM, DER, or SMIME from the Encoding enum"
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
# Only allow options that make sense for encryption
|
| 289 |
+
if any(
|
| 290 |
+
opt not in [PKCS7Options.Text, PKCS7Options.Binary]
|
| 291 |
+
for opt in options
|
| 292 |
+
):
|
| 293 |
+
raise ValueError(
|
| 294 |
+
"Only the following options are supported for encryption: "
|
| 295 |
+
"Text, Binary"
|
| 296 |
+
)
|
| 297 |
+
elif PKCS7Options.Text in options and PKCS7Options.Binary in options:
|
| 298 |
+
# OpenSSL accepts both options at the same time, but ignores Text.
|
| 299 |
+
# We fail defensively to avoid unexpected outputs.
|
| 300 |
+
raise ValueError(
|
| 301 |
+
"Cannot use Binary and Text options at the same time"
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
return rust_pkcs7.encrypt_and_serialize(
|
| 305 |
+
self, content_encryption_algorithm, encoding, options
|
| 306 |
+
)
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
pkcs7_decrypt_der = rust_pkcs7.decrypt_der
|
| 310 |
+
pkcs7_decrypt_pem = rust_pkcs7.decrypt_pem
|
| 311 |
+
pkcs7_decrypt_smime = rust_pkcs7.decrypt_smime
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def _smime_signed_encode(
|
| 315 |
+
data: bytes, signature: bytes, micalg: str, text_mode: bool
|
| 316 |
+
) -> bytes:
|
| 317 |
+
# This function works pretty hard to replicate what OpenSSL does
|
| 318 |
+
# precisely. For good and for ill.
|
| 319 |
+
|
| 320 |
+
m = email.message.Message()
|
| 321 |
+
m.add_header("MIME-Version", "1.0")
|
| 322 |
+
m.add_header(
|
| 323 |
+
"Content-Type",
|
| 324 |
+
"multipart/signed",
|
| 325 |
+
protocol="application/x-pkcs7-signature",
|
| 326 |
+
micalg=micalg,
|
| 327 |
+
)
|
| 328 |
+
|
| 329 |
+
m.preamble = "This is an S/MIME signed message\n"
|
| 330 |
+
|
| 331 |
+
msg_part = OpenSSLMimePart()
|
| 332 |
+
msg_part.set_payload(data)
|
| 333 |
+
if text_mode:
|
| 334 |
+
msg_part.add_header("Content-Type", "text/plain")
|
| 335 |
+
m.attach(msg_part)
|
| 336 |
+
|
| 337 |
+
sig_part = email.message.MIMEPart()
|
| 338 |
+
sig_part.add_header(
|
| 339 |
+
"Content-Type", "application/x-pkcs7-signature", name="smime.p7s"
|
| 340 |
+
)
|
| 341 |
+
sig_part.add_header("Content-Transfer-Encoding", "base64")
|
| 342 |
+
sig_part.add_header(
|
| 343 |
+
"Content-Disposition", "attachment", filename="smime.p7s"
|
| 344 |
+
)
|
| 345 |
+
sig_part.set_payload(
|
| 346 |
+
email.base64mime.body_encode(signature, maxlinelen=65)
|
| 347 |
+
)
|
| 348 |
+
del sig_part["MIME-Version"]
|
| 349 |
+
m.attach(sig_part)
|
| 350 |
+
|
| 351 |
+
fp = io.BytesIO()
|
| 352 |
+
g = email.generator.BytesGenerator(
|
| 353 |
+
fp,
|
| 354 |
+
maxheaderlen=0,
|
| 355 |
+
mangle_from_=False,
|
| 356 |
+
policy=m.policy.clone(linesep="\r\n"),
|
| 357 |
+
)
|
| 358 |
+
g.flatten(m)
|
| 359 |
+
return fp.getvalue()
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
def _smime_enveloped_encode(data: bytes) -> bytes:
|
| 363 |
+
m = email.message.Message()
|
| 364 |
+
m.add_header("MIME-Version", "1.0")
|
| 365 |
+
m.add_header("Content-Disposition", "attachment", filename="smime.p7m")
|
| 366 |
+
m.add_header(
|
| 367 |
+
"Content-Type",
|
| 368 |
+
"application/pkcs7-mime",
|
| 369 |
+
smime_type="enveloped-data",
|
| 370 |
+
name="smime.p7m",
|
| 371 |
+
)
|
| 372 |
+
m.add_header("Content-Transfer-Encoding", "base64")
|
| 373 |
+
|
| 374 |
+
m.set_payload(email.base64mime.body_encode(data, maxlinelen=65))
|
| 375 |
+
|
| 376 |
+
return m.as_bytes(policy=m.policy.clone(linesep="\n", max_line_length=0))
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
def _smime_enveloped_decode(data: bytes) -> bytes:
|
| 380 |
+
m = email.message_from_bytes(data)
|
| 381 |
+
if m.get_content_type() not in {
|
| 382 |
+
"application/x-pkcs7-mime",
|
| 383 |
+
"application/pkcs7-mime",
|
| 384 |
+
}:
|
| 385 |
+
raise ValueError("Not an S/MIME enveloped message")
|
| 386 |
+
return bytes(m.get_payload(decode=True))
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
def _smime_remove_text_headers(data: bytes) -> bytes:
|
| 390 |
+
m = email.message_from_bytes(data)
|
| 391 |
+
# Using get() instead of get_content_type() since it has None as default,
|
| 392 |
+
# where the latter has "text/plain". Both methods are case-insensitive.
|
| 393 |
+
content_type = m.get("content-type")
|
| 394 |
+
if content_type is None:
|
| 395 |
+
raise ValueError(
|
| 396 |
+
"Decrypted MIME data has no 'Content-Type' header. "
|
| 397 |
+
"Please remove the 'Text' option to parse it manually."
|
| 398 |
+
)
|
| 399 |
+
if "text/plain" not in content_type:
|
| 400 |
+
raise ValueError(
|
| 401 |
+
f"Decrypted MIME data content type is '{content_type}', not "
|
| 402 |
+
"'text/plain'. Remove the 'Text' option to parse it manually."
|
| 403 |
+
)
|
| 404 |
+
return bytes(m.get_payload(decode=True))
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
class OpenSSLMimePart(email.message.MIMEPart):
|
| 408 |
+
# A MIMEPart subclass that replicates OpenSSL's behavior of not including
|
| 409 |
+
# a newline if there are no headers.
|
| 410 |
+
def _write_headers(self, generator) -> None:
|
| 411 |
+
if list(self.raw_items()):
|
| 412 |
+
generator._write_headers(self)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/ssh.py
ADDED
|
@@ -0,0 +1,1615 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import binascii
|
| 8 |
+
import enum
|
| 9 |
+
import os
|
| 10 |
+
import re
|
| 11 |
+
import typing
|
| 12 |
+
import warnings
|
| 13 |
+
from dataclasses import dataclass
|
| 14 |
+
|
| 15 |
+
from cryptography import utils
|
| 16 |
+
from cryptography.exceptions import UnsupportedAlgorithm
|
| 17 |
+
from cryptography.hazmat.primitives import hashes
|
| 18 |
+
from cryptography.hazmat.primitives.asymmetric import (
|
| 19 |
+
dsa,
|
| 20 |
+
ec,
|
| 21 |
+
ed25519,
|
| 22 |
+
padding,
|
| 23 |
+
rsa,
|
| 24 |
+
)
|
| 25 |
+
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
|
| 26 |
+
from cryptography.hazmat.primitives.ciphers import (
|
| 27 |
+
AEADDecryptionContext,
|
| 28 |
+
Cipher,
|
| 29 |
+
algorithms,
|
| 30 |
+
modes,
|
| 31 |
+
)
|
| 32 |
+
from cryptography.hazmat.primitives.serialization import (
|
| 33 |
+
Encoding,
|
| 34 |
+
KeySerializationEncryption,
|
| 35 |
+
NoEncryption,
|
| 36 |
+
PrivateFormat,
|
| 37 |
+
PublicFormat,
|
| 38 |
+
_KeySerializationEncryption,
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
try:
|
| 42 |
+
from bcrypt import kdf as _bcrypt_kdf
|
| 43 |
+
|
| 44 |
+
_bcrypt_supported = True
|
| 45 |
+
except ImportError:
|
| 46 |
+
_bcrypt_supported = False
|
| 47 |
+
|
| 48 |
+
def _bcrypt_kdf(
|
| 49 |
+
password: bytes,
|
| 50 |
+
salt: bytes,
|
| 51 |
+
desired_key_bytes: int,
|
| 52 |
+
rounds: int,
|
| 53 |
+
ignore_few_rounds: bool = False,
|
| 54 |
+
) -> bytes:
|
| 55 |
+
raise UnsupportedAlgorithm("Need bcrypt module")
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
_SSH_ED25519 = b"ssh-ed25519"
|
| 59 |
+
_SSH_RSA = b"ssh-rsa"
|
| 60 |
+
_SSH_DSA = b"ssh-dss"
|
| 61 |
+
_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256"
|
| 62 |
+
_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384"
|
| 63 |
+
_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521"
|
| 64 |
+
_CERT_SUFFIX = b"-cert-v01@openssh.com"
|
| 65 |
+
|
| 66 |
+
# U2F application string suffixed pubkey
|
| 67 |
+
_SK_SSH_ED25519 = b"sk-ssh-ed25519@openssh.com"
|
| 68 |
+
_SK_SSH_ECDSA_NISTP256 = b"sk-ecdsa-sha2-nistp256@openssh.com"
|
| 69 |
+
|
| 70 |
+
# These are not key types, only algorithms, so they cannot appear
|
| 71 |
+
# as a public key type
|
| 72 |
+
_SSH_RSA_SHA256 = b"rsa-sha2-256"
|
| 73 |
+
_SSH_RSA_SHA512 = b"rsa-sha2-512"
|
| 74 |
+
|
| 75 |
+
_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)")
|
| 76 |
+
_SK_MAGIC = b"openssh-key-v1\0"
|
| 77 |
+
_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----"
|
| 78 |
+
_SK_END = b"-----END OPENSSH PRIVATE KEY-----"
|
| 79 |
+
_BCRYPT = b"bcrypt"
|
| 80 |
+
_NONE = b"none"
|
| 81 |
+
_DEFAULT_CIPHER = b"aes256-ctr"
|
| 82 |
+
_DEFAULT_ROUNDS = 16
|
| 83 |
+
|
| 84 |
+
# re is only way to work on bytes-like data
|
| 85 |
+
_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL)
|
| 86 |
+
|
| 87 |
+
# padding for max blocksize
|
| 88 |
+
_PADDING = memoryview(bytearray(range(1, 1 + 16)))
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
@dataclass
|
| 92 |
+
class _SSHCipher:
|
| 93 |
+
alg: type[algorithms.AES]
|
| 94 |
+
key_len: int
|
| 95 |
+
mode: type[modes.CTR] | type[modes.CBC] | type[modes.GCM]
|
| 96 |
+
block_len: int
|
| 97 |
+
iv_len: int
|
| 98 |
+
tag_len: int | None
|
| 99 |
+
is_aead: bool
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
# ciphers that are actually used in key wrapping
|
| 103 |
+
_SSH_CIPHERS: dict[bytes, _SSHCipher] = {
|
| 104 |
+
b"aes256-ctr": _SSHCipher(
|
| 105 |
+
alg=algorithms.AES,
|
| 106 |
+
key_len=32,
|
| 107 |
+
mode=modes.CTR,
|
| 108 |
+
block_len=16,
|
| 109 |
+
iv_len=16,
|
| 110 |
+
tag_len=None,
|
| 111 |
+
is_aead=False,
|
| 112 |
+
),
|
| 113 |
+
b"aes256-cbc": _SSHCipher(
|
| 114 |
+
alg=algorithms.AES,
|
| 115 |
+
key_len=32,
|
| 116 |
+
mode=modes.CBC,
|
| 117 |
+
block_len=16,
|
| 118 |
+
iv_len=16,
|
| 119 |
+
tag_len=None,
|
| 120 |
+
is_aead=False,
|
| 121 |
+
),
|
| 122 |
+
b"aes256-gcm@openssh.com": _SSHCipher(
|
| 123 |
+
alg=algorithms.AES,
|
| 124 |
+
key_len=32,
|
| 125 |
+
mode=modes.GCM,
|
| 126 |
+
block_len=16,
|
| 127 |
+
iv_len=12,
|
| 128 |
+
tag_len=16,
|
| 129 |
+
is_aead=True,
|
| 130 |
+
),
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
# map local curve name to key type
|
| 134 |
+
_ECDSA_KEY_TYPE = {
|
| 135 |
+
"secp256r1": _ECDSA_NISTP256,
|
| 136 |
+
"secp384r1": _ECDSA_NISTP384,
|
| 137 |
+
"secp521r1": _ECDSA_NISTP521,
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def _get_ssh_key_type(key: SSHPrivateKeyTypes | SSHPublicKeyTypes) -> bytes:
|
| 142 |
+
if isinstance(key, ec.EllipticCurvePrivateKey):
|
| 143 |
+
key_type = _ecdsa_key_type(key.public_key())
|
| 144 |
+
elif isinstance(key, ec.EllipticCurvePublicKey):
|
| 145 |
+
key_type = _ecdsa_key_type(key)
|
| 146 |
+
elif isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
|
| 147 |
+
key_type = _SSH_RSA
|
| 148 |
+
elif isinstance(key, (dsa.DSAPrivateKey, dsa.DSAPublicKey)):
|
| 149 |
+
key_type = _SSH_DSA
|
| 150 |
+
elif isinstance(
|
| 151 |
+
key, (ed25519.Ed25519PrivateKey, ed25519.Ed25519PublicKey)
|
| 152 |
+
):
|
| 153 |
+
key_type = _SSH_ED25519
|
| 154 |
+
else:
|
| 155 |
+
raise ValueError("Unsupported key type")
|
| 156 |
+
|
| 157 |
+
return key_type
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes:
|
| 161 |
+
"""Return SSH key_type and curve_name for private key."""
|
| 162 |
+
curve = public_key.curve
|
| 163 |
+
if curve.name not in _ECDSA_KEY_TYPE:
|
| 164 |
+
raise ValueError(
|
| 165 |
+
f"Unsupported curve for ssh private key: {curve.name!r}"
|
| 166 |
+
)
|
| 167 |
+
return _ECDSA_KEY_TYPE[curve.name]
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
def _check_block_size(data: utils.Buffer, block_len: int) -> None:
|
| 171 |
+
"""Require data to be full blocks"""
|
| 172 |
+
if not data or len(data) % block_len != 0:
|
| 173 |
+
raise ValueError("Corrupt data: missing padding")
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def _check_empty(data: utils.Buffer) -> None:
|
| 177 |
+
"""All data should have been parsed."""
|
| 178 |
+
if data:
|
| 179 |
+
raise ValueError("Corrupt data: unparsed data")
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def _init_cipher(
|
| 183 |
+
ciphername: bytes,
|
| 184 |
+
password: bytes | None,
|
| 185 |
+
salt: bytes,
|
| 186 |
+
rounds: int,
|
| 187 |
+
) -> Cipher[modes.CBC | modes.CTR | modes.GCM]:
|
| 188 |
+
"""Generate key + iv and return cipher."""
|
| 189 |
+
if not password:
|
| 190 |
+
raise TypeError(
|
| 191 |
+
"Key is password-protected, but password was not provided."
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
ciph = _SSH_CIPHERS[ciphername]
|
| 195 |
+
seed = _bcrypt_kdf(
|
| 196 |
+
password, salt, ciph.key_len + ciph.iv_len, rounds, True
|
| 197 |
+
)
|
| 198 |
+
return Cipher(
|
| 199 |
+
ciph.alg(seed[: ciph.key_len]),
|
| 200 |
+
ciph.mode(seed[ciph.key_len :]),
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def _get_u32(data: memoryview) -> tuple[int, memoryview]:
|
| 205 |
+
"""Uint32"""
|
| 206 |
+
if len(data) < 4:
|
| 207 |
+
raise ValueError("Invalid data")
|
| 208 |
+
return int.from_bytes(data[:4], byteorder="big"), data[4:]
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def _get_u64(data: memoryview) -> tuple[int, memoryview]:
|
| 212 |
+
"""Uint64"""
|
| 213 |
+
if len(data) < 8:
|
| 214 |
+
raise ValueError("Invalid data")
|
| 215 |
+
return int.from_bytes(data[:8], byteorder="big"), data[8:]
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def _get_sshstr(data: memoryview) -> tuple[memoryview, memoryview]:
|
| 219 |
+
"""Bytes with u32 length prefix"""
|
| 220 |
+
n, data = _get_u32(data)
|
| 221 |
+
if n > len(data):
|
| 222 |
+
raise ValueError("Invalid data")
|
| 223 |
+
return data[:n], data[n:]
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def _get_mpint(data: memoryview) -> tuple[int, memoryview]:
|
| 227 |
+
"""Big integer."""
|
| 228 |
+
val, data = _get_sshstr(data)
|
| 229 |
+
if val and val[0] > 0x7F:
|
| 230 |
+
raise ValueError("Invalid data")
|
| 231 |
+
return int.from_bytes(val, "big"), data
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
def _to_mpint(val: int) -> bytes:
|
| 235 |
+
"""Storage format for signed bigint."""
|
| 236 |
+
if val < 0:
|
| 237 |
+
raise ValueError("negative mpint not allowed")
|
| 238 |
+
if not val:
|
| 239 |
+
return b""
|
| 240 |
+
nbytes = (val.bit_length() + 8) // 8
|
| 241 |
+
return utils.int_to_bytes(val, nbytes)
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
class _FragList:
|
| 245 |
+
"""Build recursive structure without data copy."""
|
| 246 |
+
|
| 247 |
+
flist: list[utils.Buffer]
|
| 248 |
+
|
| 249 |
+
def __init__(self, init: list[utils.Buffer] | None = None) -> None:
|
| 250 |
+
self.flist = []
|
| 251 |
+
if init:
|
| 252 |
+
self.flist.extend(init)
|
| 253 |
+
|
| 254 |
+
def put_raw(self, val: utils.Buffer) -> None:
|
| 255 |
+
"""Add plain bytes"""
|
| 256 |
+
self.flist.append(val)
|
| 257 |
+
|
| 258 |
+
def put_u32(self, val: int) -> None:
|
| 259 |
+
"""Big-endian uint32"""
|
| 260 |
+
self.flist.append(val.to_bytes(length=4, byteorder="big"))
|
| 261 |
+
|
| 262 |
+
def put_u64(self, val: int) -> None:
|
| 263 |
+
"""Big-endian uint64"""
|
| 264 |
+
self.flist.append(val.to_bytes(length=8, byteorder="big"))
|
| 265 |
+
|
| 266 |
+
def put_sshstr(self, val: bytes | _FragList) -> None:
|
| 267 |
+
"""Bytes prefixed with u32 length"""
|
| 268 |
+
if isinstance(val, (bytes, memoryview, bytearray)):
|
| 269 |
+
self.put_u32(len(val))
|
| 270 |
+
self.flist.append(val)
|
| 271 |
+
else:
|
| 272 |
+
self.put_u32(val.size())
|
| 273 |
+
self.flist.extend(val.flist)
|
| 274 |
+
|
| 275 |
+
def put_mpint(self, val: int) -> None:
|
| 276 |
+
"""Big-endian bigint prefixed with u32 length"""
|
| 277 |
+
self.put_sshstr(_to_mpint(val))
|
| 278 |
+
|
| 279 |
+
def size(self) -> int:
|
| 280 |
+
"""Current number of bytes"""
|
| 281 |
+
return sum(map(len, self.flist))
|
| 282 |
+
|
| 283 |
+
def render(self, dstbuf: memoryview, pos: int = 0) -> int:
|
| 284 |
+
"""Write into bytearray"""
|
| 285 |
+
for frag in self.flist:
|
| 286 |
+
flen = len(frag)
|
| 287 |
+
start, pos = pos, pos + flen
|
| 288 |
+
dstbuf[start:pos] = frag
|
| 289 |
+
return pos
|
| 290 |
+
|
| 291 |
+
def tobytes(self) -> bytes:
|
| 292 |
+
"""Return as bytes"""
|
| 293 |
+
buf = memoryview(bytearray(self.size()))
|
| 294 |
+
self.render(buf)
|
| 295 |
+
return buf.tobytes()
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
class _SSHFormatRSA:
|
| 299 |
+
"""Format for RSA keys.
|
| 300 |
+
|
| 301 |
+
Public:
|
| 302 |
+
mpint e, n
|
| 303 |
+
Private:
|
| 304 |
+
mpint n, e, d, iqmp, p, q
|
| 305 |
+
"""
|
| 306 |
+
|
| 307 |
+
def get_public(
|
| 308 |
+
self, data: memoryview
|
| 309 |
+
) -> tuple[tuple[int, int], memoryview]:
|
| 310 |
+
"""RSA public fields"""
|
| 311 |
+
e, data = _get_mpint(data)
|
| 312 |
+
n, data = _get_mpint(data)
|
| 313 |
+
return (e, n), data
|
| 314 |
+
|
| 315 |
+
def load_public(
|
| 316 |
+
self, data: memoryview
|
| 317 |
+
) -> tuple[rsa.RSAPublicKey, memoryview]:
|
| 318 |
+
"""Make RSA public key from data."""
|
| 319 |
+
(e, n), data = self.get_public(data)
|
| 320 |
+
public_numbers = rsa.RSAPublicNumbers(e, n)
|
| 321 |
+
public_key = public_numbers.public_key()
|
| 322 |
+
return public_key, data
|
| 323 |
+
|
| 324 |
+
def load_private(
|
| 325 |
+
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
|
| 326 |
+
) -> tuple[rsa.RSAPrivateKey, memoryview]:
|
| 327 |
+
"""Make RSA private key from data."""
|
| 328 |
+
n, data = _get_mpint(data)
|
| 329 |
+
e, data = _get_mpint(data)
|
| 330 |
+
d, data = _get_mpint(data)
|
| 331 |
+
iqmp, data = _get_mpint(data)
|
| 332 |
+
p, data = _get_mpint(data)
|
| 333 |
+
q, data = _get_mpint(data)
|
| 334 |
+
|
| 335 |
+
if (e, n) != pubfields:
|
| 336 |
+
raise ValueError("Corrupt data: rsa field mismatch")
|
| 337 |
+
dmp1 = rsa.rsa_crt_dmp1(d, p)
|
| 338 |
+
dmq1 = rsa.rsa_crt_dmq1(d, q)
|
| 339 |
+
public_numbers = rsa.RSAPublicNumbers(e, n)
|
| 340 |
+
private_numbers = rsa.RSAPrivateNumbers(
|
| 341 |
+
p, q, d, dmp1, dmq1, iqmp, public_numbers
|
| 342 |
+
)
|
| 343 |
+
private_key = private_numbers.private_key(
|
| 344 |
+
unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation
|
| 345 |
+
)
|
| 346 |
+
return private_key, data
|
| 347 |
+
|
| 348 |
+
def encode_public(
|
| 349 |
+
self, public_key: rsa.RSAPublicKey, f_pub: _FragList
|
| 350 |
+
) -> None:
|
| 351 |
+
"""Write RSA public key"""
|
| 352 |
+
pubn = public_key.public_numbers()
|
| 353 |
+
f_pub.put_mpint(pubn.e)
|
| 354 |
+
f_pub.put_mpint(pubn.n)
|
| 355 |
+
|
| 356 |
+
def encode_private(
|
| 357 |
+
self, private_key: rsa.RSAPrivateKey, f_priv: _FragList
|
| 358 |
+
) -> None:
|
| 359 |
+
"""Write RSA private key"""
|
| 360 |
+
private_numbers = private_key.private_numbers()
|
| 361 |
+
public_numbers = private_numbers.public_numbers
|
| 362 |
+
|
| 363 |
+
f_priv.put_mpint(public_numbers.n)
|
| 364 |
+
f_priv.put_mpint(public_numbers.e)
|
| 365 |
+
|
| 366 |
+
f_priv.put_mpint(private_numbers.d)
|
| 367 |
+
f_priv.put_mpint(private_numbers.iqmp)
|
| 368 |
+
f_priv.put_mpint(private_numbers.p)
|
| 369 |
+
f_priv.put_mpint(private_numbers.q)
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
class _SSHFormatDSA:
|
| 373 |
+
"""Format for DSA keys.
|
| 374 |
+
|
| 375 |
+
Public:
|
| 376 |
+
mpint p, q, g, y
|
| 377 |
+
Private:
|
| 378 |
+
mpint p, q, g, y, x
|
| 379 |
+
"""
|
| 380 |
+
|
| 381 |
+
def get_public(self, data: memoryview) -> tuple[tuple, memoryview]:
|
| 382 |
+
"""DSA public fields"""
|
| 383 |
+
p, data = _get_mpint(data)
|
| 384 |
+
q, data = _get_mpint(data)
|
| 385 |
+
g, data = _get_mpint(data)
|
| 386 |
+
y, data = _get_mpint(data)
|
| 387 |
+
return (p, q, g, y), data
|
| 388 |
+
|
| 389 |
+
def load_public(
|
| 390 |
+
self, data: memoryview
|
| 391 |
+
) -> tuple[dsa.DSAPublicKey, memoryview]:
|
| 392 |
+
"""Make DSA public key from data."""
|
| 393 |
+
(p, q, g, y), data = self.get_public(data)
|
| 394 |
+
parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
|
| 395 |
+
public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
|
| 396 |
+
self._validate(public_numbers)
|
| 397 |
+
public_key = public_numbers.public_key()
|
| 398 |
+
return public_key, data
|
| 399 |
+
|
| 400 |
+
def load_private(
|
| 401 |
+
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
|
| 402 |
+
) -> tuple[dsa.DSAPrivateKey, memoryview]:
|
| 403 |
+
"""Make DSA private key from data."""
|
| 404 |
+
(p, q, g, y), data = self.get_public(data)
|
| 405 |
+
x, data = _get_mpint(data)
|
| 406 |
+
|
| 407 |
+
if (p, q, g, y) != pubfields:
|
| 408 |
+
raise ValueError("Corrupt data: dsa field mismatch")
|
| 409 |
+
parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
|
| 410 |
+
public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
|
| 411 |
+
self._validate(public_numbers)
|
| 412 |
+
private_numbers = dsa.DSAPrivateNumbers(x, public_numbers)
|
| 413 |
+
private_key = private_numbers.private_key()
|
| 414 |
+
return private_key, data
|
| 415 |
+
|
| 416 |
+
def encode_public(
|
| 417 |
+
self, public_key: dsa.DSAPublicKey, f_pub: _FragList
|
| 418 |
+
) -> None:
|
| 419 |
+
"""Write DSA public key"""
|
| 420 |
+
public_numbers = public_key.public_numbers()
|
| 421 |
+
parameter_numbers = public_numbers.parameter_numbers
|
| 422 |
+
self._validate(public_numbers)
|
| 423 |
+
|
| 424 |
+
f_pub.put_mpint(parameter_numbers.p)
|
| 425 |
+
f_pub.put_mpint(parameter_numbers.q)
|
| 426 |
+
f_pub.put_mpint(parameter_numbers.g)
|
| 427 |
+
f_pub.put_mpint(public_numbers.y)
|
| 428 |
+
|
| 429 |
+
def encode_private(
|
| 430 |
+
self, private_key: dsa.DSAPrivateKey, f_priv: _FragList
|
| 431 |
+
) -> None:
|
| 432 |
+
"""Write DSA private key"""
|
| 433 |
+
self.encode_public(private_key.public_key(), f_priv)
|
| 434 |
+
f_priv.put_mpint(private_key.private_numbers().x)
|
| 435 |
+
|
| 436 |
+
def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None:
|
| 437 |
+
parameter_numbers = public_numbers.parameter_numbers
|
| 438 |
+
if parameter_numbers.p.bit_length() != 1024:
|
| 439 |
+
raise ValueError("SSH supports only 1024 bit DSA keys")
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
class _SSHFormatECDSA:
|
| 443 |
+
"""Format for ECDSA keys.
|
| 444 |
+
|
| 445 |
+
Public:
|
| 446 |
+
str curve
|
| 447 |
+
bytes point
|
| 448 |
+
Private:
|
| 449 |
+
str curve
|
| 450 |
+
bytes point
|
| 451 |
+
mpint secret
|
| 452 |
+
"""
|
| 453 |
+
|
| 454 |
+
def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve):
|
| 455 |
+
self.ssh_curve_name = ssh_curve_name
|
| 456 |
+
self.curve = curve
|
| 457 |
+
|
| 458 |
+
def get_public(
|
| 459 |
+
self, data: memoryview
|
| 460 |
+
) -> tuple[tuple[memoryview, memoryview], memoryview]:
|
| 461 |
+
"""ECDSA public fields"""
|
| 462 |
+
curve, data = _get_sshstr(data)
|
| 463 |
+
point, data = _get_sshstr(data)
|
| 464 |
+
if curve != self.ssh_curve_name:
|
| 465 |
+
raise ValueError("Curve name mismatch")
|
| 466 |
+
if len(point) == 0:
|
| 467 |
+
raise ValueError("Invalid EC point: empty data")
|
| 468 |
+
if point[0] != 4:
|
| 469 |
+
raise NotImplementedError("Need uncompressed point")
|
| 470 |
+
return (curve, point), data
|
| 471 |
+
|
| 472 |
+
def load_public(
|
| 473 |
+
self, data: memoryview
|
| 474 |
+
) -> tuple[ec.EllipticCurvePublicKey, memoryview]:
|
| 475 |
+
"""Make ECDSA public key from data."""
|
| 476 |
+
(_, point), data = self.get_public(data)
|
| 477 |
+
public_key = ec.EllipticCurvePublicKey.from_encoded_point(
|
| 478 |
+
self.curve, point.tobytes()
|
| 479 |
+
)
|
| 480 |
+
return public_key, data
|
| 481 |
+
|
| 482 |
+
def load_private(
|
| 483 |
+
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
|
| 484 |
+
) -> tuple[ec.EllipticCurvePrivateKey, memoryview]:
|
| 485 |
+
"""Make ECDSA private key from data."""
|
| 486 |
+
(curve_name, point), data = self.get_public(data)
|
| 487 |
+
secret, data = _get_mpint(data)
|
| 488 |
+
|
| 489 |
+
if (curve_name, point) != pubfields:
|
| 490 |
+
raise ValueError("Corrupt data: ecdsa field mismatch")
|
| 491 |
+
private_key = ec.derive_private_key(secret, self.curve)
|
| 492 |
+
return private_key, data
|
| 493 |
+
|
| 494 |
+
def encode_public(
|
| 495 |
+
self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList
|
| 496 |
+
) -> None:
|
| 497 |
+
"""Write ECDSA public key"""
|
| 498 |
+
point = public_key.public_bytes(
|
| 499 |
+
Encoding.X962, PublicFormat.UncompressedPoint
|
| 500 |
+
)
|
| 501 |
+
f_pub.put_sshstr(self.ssh_curve_name)
|
| 502 |
+
f_pub.put_sshstr(point)
|
| 503 |
+
|
| 504 |
+
def encode_private(
|
| 505 |
+
self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList
|
| 506 |
+
) -> None:
|
| 507 |
+
"""Write ECDSA private key"""
|
| 508 |
+
public_key = private_key.public_key()
|
| 509 |
+
private_numbers = private_key.private_numbers()
|
| 510 |
+
|
| 511 |
+
self.encode_public(public_key, f_priv)
|
| 512 |
+
f_priv.put_mpint(private_numbers.private_value)
|
| 513 |
+
|
| 514 |
+
|
| 515 |
+
class _SSHFormatEd25519:
|
| 516 |
+
"""Format for Ed25519 keys.
|
| 517 |
+
|
| 518 |
+
Public:
|
| 519 |
+
bytes point
|
| 520 |
+
Private:
|
| 521 |
+
bytes point
|
| 522 |
+
bytes secret_and_point
|
| 523 |
+
"""
|
| 524 |
+
|
| 525 |
+
def get_public(
|
| 526 |
+
self, data: memoryview
|
| 527 |
+
) -> tuple[tuple[memoryview], memoryview]:
|
| 528 |
+
"""Ed25519 public fields"""
|
| 529 |
+
point, data = _get_sshstr(data)
|
| 530 |
+
return (point,), data
|
| 531 |
+
|
| 532 |
+
def load_public(
|
| 533 |
+
self, data: memoryview
|
| 534 |
+
) -> tuple[ed25519.Ed25519PublicKey, memoryview]:
|
| 535 |
+
"""Make Ed25519 public key from data."""
|
| 536 |
+
(point,), data = self.get_public(data)
|
| 537 |
+
public_key = ed25519.Ed25519PublicKey.from_public_bytes(
|
| 538 |
+
point.tobytes()
|
| 539 |
+
)
|
| 540 |
+
return public_key, data
|
| 541 |
+
|
| 542 |
+
def load_private(
|
| 543 |
+
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
|
| 544 |
+
) -> tuple[ed25519.Ed25519PrivateKey, memoryview]:
|
| 545 |
+
"""Make Ed25519 private key from data."""
|
| 546 |
+
(point,), data = self.get_public(data)
|
| 547 |
+
keypair, data = _get_sshstr(data)
|
| 548 |
+
|
| 549 |
+
secret = keypair[:32]
|
| 550 |
+
point2 = keypair[32:]
|
| 551 |
+
if point != point2 or (point,) != pubfields:
|
| 552 |
+
raise ValueError("Corrupt data: ed25519 field mismatch")
|
| 553 |
+
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret)
|
| 554 |
+
return private_key, data
|
| 555 |
+
|
| 556 |
+
def encode_public(
|
| 557 |
+
self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList
|
| 558 |
+
) -> None:
|
| 559 |
+
"""Write Ed25519 public key"""
|
| 560 |
+
raw_public_key = public_key.public_bytes(
|
| 561 |
+
Encoding.Raw, PublicFormat.Raw
|
| 562 |
+
)
|
| 563 |
+
f_pub.put_sshstr(raw_public_key)
|
| 564 |
+
|
| 565 |
+
def encode_private(
|
| 566 |
+
self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList
|
| 567 |
+
) -> None:
|
| 568 |
+
"""Write Ed25519 private key"""
|
| 569 |
+
public_key = private_key.public_key()
|
| 570 |
+
raw_private_key = private_key.private_bytes(
|
| 571 |
+
Encoding.Raw, PrivateFormat.Raw, NoEncryption()
|
| 572 |
+
)
|
| 573 |
+
raw_public_key = public_key.public_bytes(
|
| 574 |
+
Encoding.Raw, PublicFormat.Raw
|
| 575 |
+
)
|
| 576 |
+
f_keypair = _FragList([raw_private_key, raw_public_key])
|
| 577 |
+
|
| 578 |
+
self.encode_public(public_key, f_priv)
|
| 579 |
+
f_priv.put_sshstr(f_keypair)
|
| 580 |
+
|
| 581 |
+
|
| 582 |
+
def load_application(data) -> tuple[memoryview, memoryview]:
|
| 583 |
+
"""
|
| 584 |
+
U2F application strings
|
| 585 |
+
"""
|
| 586 |
+
application, data = _get_sshstr(data)
|
| 587 |
+
if not application.tobytes().startswith(b"ssh:"):
|
| 588 |
+
raise ValueError(
|
| 589 |
+
"U2F application string does not start with b'ssh:' "
|
| 590 |
+
f"({application})"
|
| 591 |
+
)
|
| 592 |
+
return application, data
|
| 593 |
+
|
| 594 |
+
|
| 595 |
+
class _SSHFormatSKEd25519:
|
| 596 |
+
"""
|
| 597 |
+
The format of a sk-ssh-ed25519@openssh.com public key is:
|
| 598 |
+
|
| 599 |
+
string "sk-ssh-ed25519@openssh.com"
|
| 600 |
+
string public key
|
| 601 |
+
string application (user-specified, but typically "ssh:")
|
| 602 |
+
"""
|
| 603 |
+
|
| 604 |
+
def load_public(
|
| 605 |
+
self, data: memoryview
|
| 606 |
+
) -> tuple[ed25519.Ed25519PublicKey, memoryview]:
|
| 607 |
+
"""Make Ed25519 public key from data."""
|
| 608 |
+
public_key, data = _lookup_kformat(_SSH_ED25519).load_public(data)
|
| 609 |
+
_, data = load_application(data)
|
| 610 |
+
return public_key, data
|
| 611 |
+
|
| 612 |
+
def get_public(self, data: memoryview) -> typing.NoReturn:
|
| 613 |
+
# Confusingly `get_public` is an entry point used by private key
|
| 614 |
+
# loading.
|
| 615 |
+
raise UnsupportedAlgorithm(
|
| 616 |
+
"sk-ssh-ed25519 private keys cannot be loaded"
|
| 617 |
+
)
|
| 618 |
+
|
| 619 |
+
|
| 620 |
+
class _SSHFormatSKECDSA:
|
| 621 |
+
"""
|
| 622 |
+
The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:
|
| 623 |
+
|
| 624 |
+
string "sk-ecdsa-sha2-nistp256@openssh.com"
|
| 625 |
+
string curve name
|
| 626 |
+
ec_point Q
|
| 627 |
+
string application (user-specified, but typically "ssh:")
|
| 628 |
+
"""
|
| 629 |
+
|
| 630 |
+
def load_public(
|
| 631 |
+
self, data: memoryview
|
| 632 |
+
) -> tuple[ec.EllipticCurvePublicKey, memoryview]:
|
| 633 |
+
"""Make ECDSA public key from data."""
|
| 634 |
+
public_key, data = _lookup_kformat(_ECDSA_NISTP256).load_public(data)
|
| 635 |
+
_, data = load_application(data)
|
| 636 |
+
return public_key, data
|
| 637 |
+
|
| 638 |
+
def get_public(self, data: memoryview) -> typing.NoReturn:
|
| 639 |
+
# Confusingly `get_public` is an entry point used by private key
|
| 640 |
+
# loading.
|
| 641 |
+
raise UnsupportedAlgorithm(
|
| 642 |
+
"sk-ecdsa-sha2-nistp256 private keys cannot be loaded"
|
| 643 |
+
)
|
| 644 |
+
|
| 645 |
+
|
| 646 |
+
_KEY_FORMATS = {
|
| 647 |
+
_SSH_RSA: _SSHFormatRSA(),
|
| 648 |
+
_SSH_DSA: _SSHFormatDSA(),
|
| 649 |
+
_SSH_ED25519: _SSHFormatEd25519(),
|
| 650 |
+
_ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()),
|
| 651 |
+
_ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()),
|
| 652 |
+
_ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()),
|
| 653 |
+
_SK_SSH_ED25519: _SSHFormatSKEd25519(),
|
| 654 |
+
_SK_SSH_ECDSA_NISTP256: _SSHFormatSKECDSA(),
|
| 655 |
+
}
|
| 656 |
+
|
| 657 |
+
|
| 658 |
+
def _lookup_kformat(key_type: utils.Buffer):
|
| 659 |
+
"""Return valid format or throw error"""
|
| 660 |
+
if not isinstance(key_type, bytes):
|
| 661 |
+
key_type = memoryview(key_type).tobytes()
|
| 662 |
+
if key_type in _KEY_FORMATS:
|
| 663 |
+
return _KEY_FORMATS[key_type]
|
| 664 |
+
raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}")
|
| 665 |
+
|
| 666 |
+
|
| 667 |
+
SSHPrivateKeyTypes = typing.Union[
|
| 668 |
+
ec.EllipticCurvePrivateKey,
|
| 669 |
+
rsa.RSAPrivateKey,
|
| 670 |
+
dsa.DSAPrivateKey,
|
| 671 |
+
ed25519.Ed25519PrivateKey,
|
| 672 |
+
]
|
| 673 |
+
|
| 674 |
+
|
| 675 |
+
def load_ssh_private_key(
|
| 676 |
+
data: utils.Buffer,
|
| 677 |
+
password: bytes | None,
|
| 678 |
+
backend: typing.Any = None,
|
| 679 |
+
*,
|
| 680 |
+
unsafe_skip_rsa_key_validation: bool = False,
|
| 681 |
+
) -> SSHPrivateKeyTypes:
|
| 682 |
+
"""Load private key from OpenSSH custom encoding."""
|
| 683 |
+
utils._check_byteslike("data", data)
|
| 684 |
+
if password is not None:
|
| 685 |
+
utils._check_bytes("password", password)
|
| 686 |
+
|
| 687 |
+
m = _PEM_RC.search(data)
|
| 688 |
+
if not m:
|
| 689 |
+
raise ValueError("Not OpenSSH private key format")
|
| 690 |
+
p1 = m.start(1)
|
| 691 |
+
p2 = m.end(1)
|
| 692 |
+
data = binascii.a2b_base64(memoryview(data)[p1:p2])
|
| 693 |
+
if not data.startswith(_SK_MAGIC):
|
| 694 |
+
raise ValueError("Not OpenSSH private key format")
|
| 695 |
+
data = memoryview(data)[len(_SK_MAGIC) :]
|
| 696 |
+
|
| 697 |
+
# parse header
|
| 698 |
+
ciphername, data = _get_sshstr(data)
|
| 699 |
+
kdfname, data = _get_sshstr(data)
|
| 700 |
+
kdfoptions, data = _get_sshstr(data)
|
| 701 |
+
nkeys, data = _get_u32(data)
|
| 702 |
+
if nkeys != 1:
|
| 703 |
+
raise ValueError("Only one key supported")
|
| 704 |
+
|
| 705 |
+
# load public key data
|
| 706 |
+
pubdata, data = _get_sshstr(data)
|
| 707 |
+
pub_key_type, pubdata = _get_sshstr(pubdata)
|
| 708 |
+
kformat = _lookup_kformat(pub_key_type)
|
| 709 |
+
pubfields, pubdata = kformat.get_public(pubdata)
|
| 710 |
+
_check_empty(pubdata)
|
| 711 |
+
|
| 712 |
+
if ciphername != _NONE or kdfname != _NONE:
|
| 713 |
+
ciphername_bytes = ciphername.tobytes()
|
| 714 |
+
if ciphername_bytes not in _SSH_CIPHERS:
|
| 715 |
+
raise UnsupportedAlgorithm(
|
| 716 |
+
f"Unsupported cipher: {ciphername_bytes!r}"
|
| 717 |
+
)
|
| 718 |
+
if kdfname != _BCRYPT:
|
| 719 |
+
raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}")
|
| 720 |
+
blklen = _SSH_CIPHERS[ciphername_bytes].block_len
|
| 721 |
+
tag_len = _SSH_CIPHERS[ciphername_bytes].tag_len
|
| 722 |
+
# load secret data
|
| 723 |
+
edata, data = _get_sshstr(data)
|
| 724 |
+
# see https://bugzilla.mindrot.org/show_bug.cgi?id=3553 for
|
| 725 |
+
# information about how OpenSSH handles AEAD tags
|
| 726 |
+
if _SSH_CIPHERS[ciphername_bytes].is_aead:
|
| 727 |
+
tag = bytes(data)
|
| 728 |
+
if len(tag) != tag_len:
|
| 729 |
+
raise ValueError("Corrupt data: invalid tag length for cipher")
|
| 730 |
+
else:
|
| 731 |
+
_check_empty(data)
|
| 732 |
+
_check_block_size(edata, blklen)
|
| 733 |
+
salt, kbuf = _get_sshstr(kdfoptions)
|
| 734 |
+
rounds, kbuf = _get_u32(kbuf)
|
| 735 |
+
_check_empty(kbuf)
|
| 736 |
+
ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds)
|
| 737 |
+
dec = ciph.decryptor()
|
| 738 |
+
edata = memoryview(dec.update(edata))
|
| 739 |
+
if _SSH_CIPHERS[ciphername_bytes].is_aead:
|
| 740 |
+
assert isinstance(dec, AEADDecryptionContext)
|
| 741 |
+
_check_empty(dec.finalize_with_tag(tag))
|
| 742 |
+
else:
|
| 743 |
+
# _check_block_size requires data to be a full block so there
|
| 744 |
+
# should be no output from finalize
|
| 745 |
+
_check_empty(dec.finalize())
|
| 746 |
+
else:
|
| 747 |
+
if password:
|
| 748 |
+
raise TypeError(
|
| 749 |
+
"Password was given but private key is not encrypted."
|
| 750 |
+
)
|
| 751 |
+
# load secret data
|
| 752 |
+
edata, data = _get_sshstr(data)
|
| 753 |
+
_check_empty(data)
|
| 754 |
+
blklen = 8
|
| 755 |
+
_check_block_size(edata, blklen)
|
| 756 |
+
ck1, edata = _get_u32(edata)
|
| 757 |
+
ck2, edata = _get_u32(edata)
|
| 758 |
+
if ck1 != ck2:
|
| 759 |
+
raise ValueError("Corrupt data: broken checksum")
|
| 760 |
+
|
| 761 |
+
# load per-key struct
|
| 762 |
+
key_type, edata = _get_sshstr(edata)
|
| 763 |
+
if key_type != pub_key_type:
|
| 764 |
+
raise ValueError("Corrupt data: key type mismatch")
|
| 765 |
+
private_key, edata = kformat.load_private(
|
| 766 |
+
edata,
|
| 767 |
+
pubfields,
|
| 768 |
+
unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation,
|
| 769 |
+
)
|
| 770 |
+
# We don't use the comment
|
| 771 |
+
_, edata = _get_sshstr(edata)
|
| 772 |
+
|
| 773 |
+
# yes, SSH does padding check *after* all other parsing is done.
|
| 774 |
+
# need to follow as it writes zero-byte padding too.
|
| 775 |
+
if edata != _PADDING[: len(edata)]:
|
| 776 |
+
raise ValueError("Corrupt data: invalid padding")
|
| 777 |
+
|
| 778 |
+
if isinstance(private_key, dsa.DSAPrivateKey):
|
| 779 |
+
warnings.warn(
|
| 780 |
+
"SSH DSA keys are deprecated and will be removed in a future "
|
| 781 |
+
"release.",
|
| 782 |
+
utils.DeprecatedIn40,
|
| 783 |
+
stacklevel=2,
|
| 784 |
+
)
|
| 785 |
+
|
| 786 |
+
return private_key
|
| 787 |
+
|
| 788 |
+
|
| 789 |
+
def _serialize_ssh_private_key(
|
| 790 |
+
private_key: SSHPrivateKeyTypes,
|
| 791 |
+
password: bytes,
|
| 792 |
+
encryption_algorithm: KeySerializationEncryption,
|
| 793 |
+
) -> bytes:
|
| 794 |
+
"""Serialize private key with OpenSSH custom encoding."""
|
| 795 |
+
utils._check_bytes("password", password)
|
| 796 |
+
if isinstance(private_key, dsa.DSAPrivateKey):
|
| 797 |
+
warnings.warn(
|
| 798 |
+
"SSH DSA key support is deprecated and will be "
|
| 799 |
+
"removed in a future release",
|
| 800 |
+
utils.DeprecatedIn40,
|
| 801 |
+
stacklevel=4,
|
| 802 |
+
)
|
| 803 |
+
|
| 804 |
+
key_type = _get_ssh_key_type(private_key)
|
| 805 |
+
kformat = _lookup_kformat(key_type)
|
| 806 |
+
|
| 807 |
+
# setup parameters
|
| 808 |
+
f_kdfoptions = _FragList()
|
| 809 |
+
if password:
|
| 810 |
+
ciphername = _DEFAULT_CIPHER
|
| 811 |
+
blklen = _SSH_CIPHERS[ciphername].block_len
|
| 812 |
+
kdfname = _BCRYPT
|
| 813 |
+
rounds = _DEFAULT_ROUNDS
|
| 814 |
+
if (
|
| 815 |
+
isinstance(encryption_algorithm, _KeySerializationEncryption)
|
| 816 |
+
and encryption_algorithm._kdf_rounds is not None
|
| 817 |
+
):
|
| 818 |
+
rounds = encryption_algorithm._kdf_rounds
|
| 819 |
+
salt = os.urandom(16)
|
| 820 |
+
f_kdfoptions.put_sshstr(salt)
|
| 821 |
+
f_kdfoptions.put_u32(rounds)
|
| 822 |
+
ciph = _init_cipher(ciphername, password, salt, rounds)
|
| 823 |
+
else:
|
| 824 |
+
ciphername = kdfname = _NONE
|
| 825 |
+
blklen = 8
|
| 826 |
+
ciph = None
|
| 827 |
+
nkeys = 1
|
| 828 |
+
checkval = os.urandom(4)
|
| 829 |
+
comment = b""
|
| 830 |
+
|
| 831 |
+
# encode public and private parts together
|
| 832 |
+
f_public_key = _FragList()
|
| 833 |
+
f_public_key.put_sshstr(key_type)
|
| 834 |
+
kformat.encode_public(private_key.public_key(), f_public_key)
|
| 835 |
+
|
| 836 |
+
f_secrets = _FragList([checkval, checkval])
|
| 837 |
+
f_secrets.put_sshstr(key_type)
|
| 838 |
+
kformat.encode_private(private_key, f_secrets)
|
| 839 |
+
f_secrets.put_sshstr(comment)
|
| 840 |
+
f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)])
|
| 841 |
+
|
| 842 |
+
# top-level structure
|
| 843 |
+
f_main = _FragList()
|
| 844 |
+
f_main.put_raw(_SK_MAGIC)
|
| 845 |
+
f_main.put_sshstr(ciphername)
|
| 846 |
+
f_main.put_sshstr(kdfname)
|
| 847 |
+
f_main.put_sshstr(f_kdfoptions)
|
| 848 |
+
f_main.put_u32(nkeys)
|
| 849 |
+
f_main.put_sshstr(f_public_key)
|
| 850 |
+
f_main.put_sshstr(f_secrets)
|
| 851 |
+
|
| 852 |
+
# copy result info bytearray
|
| 853 |
+
slen = f_secrets.size()
|
| 854 |
+
mlen = f_main.size()
|
| 855 |
+
buf = memoryview(bytearray(mlen + blklen))
|
| 856 |
+
f_main.render(buf)
|
| 857 |
+
ofs = mlen - slen
|
| 858 |
+
|
| 859 |
+
# encrypt in-place
|
| 860 |
+
if ciph is not None:
|
| 861 |
+
ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:])
|
| 862 |
+
|
| 863 |
+
return bytes(buf[:mlen])
|
| 864 |
+
|
| 865 |
+
|
| 866 |
+
SSHPublicKeyTypes = typing.Union[
|
| 867 |
+
ec.EllipticCurvePublicKey,
|
| 868 |
+
rsa.RSAPublicKey,
|
| 869 |
+
dsa.DSAPublicKey,
|
| 870 |
+
ed25519.Ed25519PublicKey,
|
| 871 |
+
]
|
| 872 |
+
|
| 873 |
+
SSHCertPublicKeyTypes = typing.Union[
|
| 874 |
+
ec.EllipticCurvePublicKey,
|
| 875 |
+
rsa.RSAPublicKey,
|
| 876 |
+
ed25519.Ed25519PublicKey,
|
| 877 |
+
]
|
| 878 |
+
|
| 879 |
+
|
| 880 |
+
class SSHCertificateType(enum.Enum):
|
| 881 |
+
USER = 1
|
| 882 |
+
HOST = 2
|
| 883 |
+
|
| 884 |
+
|
| 885 |
+
class SSHCertificate:
|
| 886 |
+
def __init__(
|
| 887 |
+
self,
|
| 888 |
+
_nonce: memoryview,
|
| 889 |
+
_public_key: SSHPublicKeyTypes,
|
| 890 |
+
_serial: int,
|
| 891 |
+
_cctype: int,
|
| 892 |
+
_key_id: memoryview,
|
| 893 |
+
_valid_principals: list[bytes],
|
| 894 |
+
_valid_after: int,
|
| 895 |
+
_valid_before: int,
|
| 896 |
+
_critical_options: dict[bytes, bytes],
|
| 897 |
+
_extensions: dict[bytes, bytes],
|
| 898 |
+
_sig_type: memoryview,
|
| 899 |
+
_sig_key: memoryview,
|
| 900 |
+
_inner_sig_type: memoryview,
|
| 901 |
+
_signature: memoryview,
|
| 902 |
+
_tbs_cert_body: memoryview,
|
| 903 |
+
_cert_key_type: bytes,
|
| 904 |
+
_cert_body: memoryview,
|
| 905 |
+
):
|
| 906 |
+
self._nonce = _nonce
|
| 907 |
+
self._public_key = _public_key
|
| 908 |
+
self._serial = _serial
|
| 909 |
+
try:
|
| 910 |
+
self._type = SSHCertificateType(_cctype)
|
| 911 |
+
except ValueError:
|
| 912 |
+
raise ValueError("Invalid certificate type")
|
| 913 |
+
self._key_id = _key_id
|
| 914 |
+
self._valid_principals = _valid_principals
|
| 915 |
+
self._valid_after = _valid_after
|
| 916 |
+
self._valid_before = _valid_before
|
| 917 |
+
self._critical_options = _critical_options
|
| 918 |
+
self._extensions = _extensions
|
| 919 |
+
self._sig_type = _sig_type
|
| 920 |
+
self._sig_key = _sig_key
|
| 921 |
+
self._inner_sig_type = _inner_sig_type
|
| 922 |
+
self._signature = _signature
|
| 923 |
+
self._cert_key_type = _cert_key_type
|
| 924 |
+
self._cert_body = _cert_body
|
| 925 |
+
self._tbs_cert_body = _tbs_cert_body
|
| 926 |
+
|
| 927 |
+
@property
|
| 928 |
+
def nonce(self) -> bytes:
|
| 929 |
+
return bytes(self._nonce)
|
| 930 |
+
|
| 931 |
+
def public_key(self) -> SSHCertPublicKeyTypes:
|
| 932 |
+
# make mypy happy until we remove DSA support entirely and
|
| 933 |
+
# the underlying union won't have a disallowed type
|
| 934 |
+
return typing.cast(SSHCertPublicKeyTypes, self._public_key)
|
| 935 |
+
|
| 936 |
+
@property
|
| 937 |
+
def serial(self) -> int:
|
| 938 |
+
return self._serial
|
| 939 |
+
|
| 940 |
+
@property
|
| 941 |
+
def type(self) -> SSHCertificateType:
|
| 942 |
+
return self._type
|
| 943 |
+
|
| 944 |
+
@property
|
| 945 |
+
def key_id(self) -> bytes:
|
| 946 |
+
return bytes(self._key_id)
|
| 947 |
+
|
| 948 |
+
@property
|
| 949 |
+
def valid_principals(self) -> list[bytes]:
|
| 950 |
+
return self._valid_principals
|
| 951 |
+
|
| 952 |
+
@property
|
| 953 |
+
def valid_before(self) -> int:
|
| 954 |
+
return self._valid_before
|
| 955 |
+
|
| 956 |
+
@property
|
| 957 |
+
def valid_after(self) -> int:
|
| 958 |
+
return self._valid_after
|
| 959 |
+
|
| 960 |
+
@property
|
| 961 |
+
def critical_options(self) -> dict[bytes, bytes]:
|
| 962 |
+
return self._critical_options
|
| 963 |
+
|
| 964 |
+
@property
|
| 965 |
+
def extensions(self) -> dict[bytes, bytes]:
|
| 966 |
+
return self._extensions
|
| 967 |
+
|
| 968 |
+
def signature_key(self) -> SSHCertPublicKeyTypes:
|
| 969 |
+
sigformat = _lookup_kformat(self._sig_type)
|
| 970 |
+
signature_key, sigkey_rest = sigformat.load_public(self._sig_key)
|
| 971 |
+
_check_empty(sigkey_rest)
|
| 972 |
+
return signature_key
|
| 973 |
+
|
| 974 |
+
def public_bytes(self) -> bytes:
|
| 975 |
+
return (
|
| 976 |
+
bytes(self._cert_key_type)
|
| 977 |
+
+ b" "
|
| 978 |
+
+ binascii.b2a_base64(bytes(self._cert_body), newline=False)
|
| 979 |
+
)
|
| 980 |
+
|
| 981 |
+
def verify_cert_signature(self) -> None:
|
| 982 |
+
signature_key = self.signature_key()
|
| 983 |
+
if isinstance(signature_key, ed25519.Ed25519PublicKey):
|
| 984 |
+
signature_key.verify(
|
| 985 |
+
bytes(self._signature), bytes(self._tbs_cert_body)
|
| 986 |
+
)
|
| 987 |
+
elif isinstance(signature_key, ec.EllipticCurvePublicKey):
|
| 988 |
+
# The signature is encoded as a pair of big-endian integers
|
| 989 |
+
r, data = _get_mpint(self._signature)
|
| 990 |
+
s, data = _get_mpint(data)
|
| 991 |
+
_check_empty(data)
|
| 992 |
+
computed_sig = asym_utils.encode_dss_signature(r, s)
|
| 993 |
+
hash_alg = _get_ec_hash_alg(signature_key.curve)
|
| 994 |
+
signature_key.verify(
|
| 995 |
+
computed_sig, bytes(self._tbs_cert_body), ec.ECDSA(hash_alg)
|
| 996 |
+
)
|
| 997 |
+
else:
|
| 998 |
+
assert isinstance(signature_key, rsa.RSAPublicKey)
|
| 999 |
+
if self._inner_sig_type == _SSH_RSA:
|
| 1000 |
+
hash_alg = hashes.SHA1()
|
| 1001 |
+
elif self._inner_sig_type == _SSH_RSA_SHA256:
|
| 1002 |
+
hash_alg = hashes.SHA256()
|
| 1003 |
+
else:
|
| 1004 |
+
assert self._inner_sig_type == _SSH_RSA_SHA512
|
| 1005 |
+
hash_alg = hashes.SHA512()
|
| 1006 |
+
signature_key.verify(
|
| 1007 |
+
bytes(self._signature),
|
| 1008 |
+
bytes(self._tbs_cert_body),
|
| 1009 |
+
padding.PKCS1v15(),
|
| 1010 |
+
hash_alg,
|
| 1011 |
+
)
|
| 1012 |
+
|
| 1013 |
+
|
| 1014 |
+
def _get_ec_hash_alg(curve: ec.EllipticCurve) -> hashes.HashAlgorithm:
|
| 1015 |
+
if isinstance(curve, ec.SECP256R1):
|
| 1016 |
+
return hashes.SHA256()
|
| 1017 |
+
elif isinstance(curve, ec.SECP384R1):
|
| 1018 |
+
return hashes.SHA384()
|
| 1019 |
+
else:
|
| 1020 |
+
assert isinstance(curve, ec.SECP521R1)
|
| 1021 |
+
return hashes.SHA512()
|
| 1022 |
+
|
| 1023 |
+
|
| 1024 |
+
def _load_ssh_public_identity(
|
| 1025 |
+
data: utils.Buffer,
|
| 1026 |
+
_legacy_dsa_allowed=False,
|
| 1027 |
+
) -> SSHCertificate | SSHPublicKeyTypes:
|
| 1028 |
+
utils._check_byteslike("data", data)
|
| 1029 |
+
|
| 1030 |
+
m = _SSH_PUBKEY_RC.match(data)
|
| 1031 |
+
if not m:
|
| 1032 |
+
raise ValueError("Invalid line format")
|
| 1033 |
+
key_type = orig_key_type = m.group(1)
|
| 1034 |
+
key_body = m.group(2)
|
| 1035 |
+
with_cert = False
|
| 1036 |
+
if key_type.endswith(_CERT_SUFFIX):
|
| 1037 |
+
with_cert = True
|
| 1038 |
+
key_type = key_type[: -len(_CERT_SUFFIX)]
|
| 1039 |
+
if key_type == _SSH_DSA and not _legacy_dsa_allowed:
|
| 1040 |
+
raise UnsupportedAlgorithm(
|
| 1041 |
+
"DSA keys aren't supported in SSH certificates"
|
| 1042 |
+
)
|
| 1043 |
+
kformat = _lookup_kformat(key_type)
|
| 1044 |
+
|
| 1045 |
+
try:
|
| 1046 |
+
rest = memoryview(binascii.a2b_base64(key_body))
|
| 1047 |
+
except (TypeError, binascii.Error):
|
| 1048 |
+
raise ValueError("Invalid format")
|
| 1049 |
+
|
| 1050 |
+
if with_cert:
|
| 1051 |
+
cert_body = rest
|
| 1052 |
+
inner_key_type, rest = _get_sshstr(rest)
|
| 1053 |
+
if inner_key_type != orig_key_type:
|
| 1054 |
+
raise ValueError("Invalid key format")
|
| 1055 |
+
if with_cert:
|
| 1056 |
+
nonce, rest = _get_sshstr(rest)
|
| 1057 |
+
public_key, rest = kformat.load_public(rest)
|
| 1058 |
+
if with_cert:
|
| 1059 |
+
serial, rest = _get_u64(rest)
|
| 1060 |
+
cctype, rest = _get_u32(rest)
|
| 1061 |
+
key_id, rest = _get_sshstr(rest)
|
| 1062 |
+
principals, rest = _get_sshstr(rest)
|
| 1063 |
+
valid_principals = []
|
| 1064 |
+
while principals:
|
| 1065 |
+
principal, principals = _get_sshstr(principals)
|
| 1066 |
+
valid_principals.append(bytes(principal))
|
| 1067 |
+
valid_after, rest = _get_u64(rest)
|
| 1068 |
+
valid_before, rest = _get_u64(rest)
|
| 1069 |
+
crit_options, rest = _get_sshstr(rest)
|
| 1070 |
+
critical_options = _parse_exts_opts(crit_options)
|
| 1071 |
+
exts, rest = _get_sshstr(rest)
|
| 1072 |
+
extensions = _parse_exts_opts(exts)
|
| 1073 |
+
# Get the reserved field, which is unused.
|
| 1074 |
+
_, rest = _get_sshstr(rest)
|
| 1075 |
+
sig_key_raw, rest = _get_sshstr(rest)
|
| 1076 |
+
sig_type, sig_key = _get_sshstr(sig_key_raw)
|
| 1077 |
+
if sig_type == _SSH_DSA and not _legacy_dsa_allowed:
|
| 1078 |
+
raise UnsupportedAlgorithm(
|
| 1079 |
+
"DSA signatures aren't supported in SSH certificates"
|
| 1080 |
+
)
|
| 1081 |
+
# Get the entire cert body and subtract the signature
|
| 1082 |
+
tbs_cert_body = cert_body[: -len(rest)]
|
| 1083 |
+
signature_raw, rest = _get_sshstr(rest)
|
| 1084 |
+
_check_empty(rest)
|
| 1085 |
+
inner_sig_type, sig_rest = _get_sshstr(signature_raw)
|
| 1086 |
+
# RSA certs can have multiple algorithm types
|
| 1087 |
+
if (
|
| 1088 |
+
sig_type == _SSH_RSA
|
| 1089 |
+
and inner_sig_type
|
| 1090 |
+
not in [_SSH_RSA_SHA256, _SSH_RSA_SHA512, _SSH_RSA]
|
| 1091 |
+
) or (sig_type != _SSH_RSA and inner_sig_type != sig_type):
|
| 1092 |
+
raise ValueError("Signature key type does not match")
|
| 1093 |
+
signature, sig_rest = _get_sshstr(sig_rest)
|
| 1094 |
+
_check_empty(sig_rest)
|
| 1095 |
+
return SSHCertificate(
|
| 1096 |
+
nonce,
|
| 1097 |
+
public_key,
|
| 1098 |
+
serial,
|
| 1099 |
+
cctype,
|
| 1100 |
+
key_id,
|
| 1101 |
+
valid_principals,
|
| 1102 |
+
valid_after,
|
| 1103 |
+
valid_before,
|
| 1104 |
+
critical_options,
|
| 1105 |
+
extensions,
|
| 1106 |
+
sig_type,
|
| 1107 |
+
sig_key,
|
| 1108 |
+
inner_sig_type,
|
| 1109 |
+
signature,
|
| 1110 |
+
tbs_cert_body,
|
| 1111 |
+
orig_key_type,
|
| 1112 |
+
cert_body,
|
| 1113 |
+
)
|
| 1114 |
+
else:
|
| 1115 |
+
_check_empty(rest)
|
| 1116 |
+
return public_key
|
| 1117 |
+
|
| 1118 |
+
|
| 1119 |
+
def load_ssh_public_identity(
|
| 1120 |
+
data: utils.Buffer,
|
| 1121 |
+
) -> SSHCertificate | SSHPublicKeyTypes:
|
| 1122 |
+
return _load_ssh_public_identity(data)
|
| 1123 |
+
|
| 1124 |
+
|
| 1125 |
+
def _parse_exts_opts(exts_opts: memoryview) -> dict[bytes, bytes]:
|
| 1126 |
+
result: dict[bytes, bytes] = {}
|
| 1127 |
+
last_name = None
|
| 1128 |
+
while exts_opts:
|
| 1129 |
+
name, exts_opts = _get_sshstr(exts_opts)
|
| 1130 |
+
bname: bytes = bytes(name)
|
| 1131 |
+
if bname in result:
|
| 1132 |
+
raise ValueError("Duplicate name")
|
| 1133 |
+
if last_name is not None and bname < last_name:
|
| 1134 |
+
raise ValueError("Fields not lexically sorted")
|
| 1135 |
+
value, exts_opts = _get_sshstr(exts_opts)
|
| 1136 |
+
if len(value) > 0:
|
| 1137 |
+
value, extra = _get_sshstr(value)
|
| 1138 |
+
if len(extra) > 0:
|
| 1139 |
+
raise ValueError("Unexpected extra data after value")
|
| 1140 |
+
result[bname] = bytes(value)
|
| 1141 |
+
last_name = bname
|
| 1142 |
+
return result
|
| 1143 |
+
|
| 1144 |
+
|
| 1145 |
+
def ssh_key_fingerprint(
|
| 1146 |
+
key: SSHPublicKeyTypes,
|
| 1147 |
+
hash_algorithm: hashes.MD5 | hashes.SHA1 | hashes.SHA256,
|
| 1148 |
+
) -> bytes:
|
| 1149 |
+
if not isinstance(
|
| 1150 |
+
hash_algorithm,
|
| 1151 |
+
(hashes.MD5, hashes.SHA1, hashes.SHA256),
|
| 1152 |
+
):
|
| 1153 |
+
raise TypeError("hash_algorithm must be either MD5, SHA1, or SHA256")
|
| 1154 |
+
|
| 1155 |
+
key_type = _get_ssh_key_type(key)
|
| 1156 |
+
kformat = _lookup_kformat(key_type)
|
| 1157 |
+
|
| 1158 |
+
f_pub = _FragList()
|
| 1159 |
+
f_pub.put_sshstr(key_type)
|
| 1160 |
+
kformat.encode_public(key, f_pub)
|
| 1161 |
+
|
| 1162 |
+
ssh_binary_data = f_pub.tobytes()
|
| 1163 |
+
|
| 1164 |
+
# Hash the binary data
|
| 1165 |
+
hash_obj = hashes.Hash(hash_algorithm)
|
| 1166 |
+
hash_obj.update(ssh_binary_data)
|
| 1167 |
+
return hash_obj.finalize()
|
| 1168 |
+
|
| 1169 |
+
|
| 1170 |
+
def load_ssh_public_key(
|
| 1171 |
+
data: utils.Buffer, backend: typing.Any = None
|
| 1172 |
+
) -> SSHPublicKeyTypes:
|
| 1173 |
+
cert_or_key = _load_ssh_public_identity(data, _legacy_dsa_allowed=True)
|
| 1174 |
+
public_key: SSHPublicKeyTypes
|
| 1175 |
+
if isinstance(cert_or_key, SSHCertificate):
|
| 1176 |
+
public_key = cert_or_key.public_key()
|
| 1177 |
+
else:
|
| 1178 |
+
public_key = cert_or_key
|
| 1179 |
+
|
| 1180 |
+
if isinstance(public_key, dsa.DSAPublicKey):
|
| 1181 |
+
warnings.warn(
|
| 1182 |
+
"SSH DSA keys are deprecated and will be removed in a future "
|
| 1183 |
+
"release.",
|
| 1184 |
+
utils.DeprecatedIn40,
|
| 1185 |
+
stacklevel=2,
|
| 1186 |
+
)
|
| 1187 |
+
return public_key
|
| 1188 |
+
|
| 1189 |
+
|
| 1190 |
+
def serialize_ssh_public_key(public_key: SSHPublicKeyTypes) -> bytes:
|
| 1191 |
+
"""One-line public key format for OpenSSH"""
|
| 1192 |
+
if isinstance(public_key, dsa.DSAPublicKey):
|
| 1193 |
+
warnings.warn(
|
| 1194 |
+
"SSH DSA key support is deprecated and will be "
|
| 1195 |
+
"removed in a future release",
|
| 1196 |
+
utils.DeprecatedIn40,
|
| 1197 |
+
stacklevel=4,
|
| 1198 |
+
)
|
| 1199 |
+
key_type = _get_ssh_key_type(public_key)
|
| 1200 |
+
kformat = _lookup_kformat(key_type)
|
| 1201 |
+
|
| 1202 |
+
f_pub = _FragList()
|
| 1203 |
+
f_pub.put_sshstr(key_type)
|
| 1204 |
+
kformat.encode_public(public_key, f_pub)
|
| 1205 |
+
|
| 1206 |
+
pub = binascii.b2a_base64(f_pub.tobytes()).strip()
|
| 1207 |
+
return b"".join([key_type, b" ", pub])
|
| 1208 |
+
|
| 1209 |
+
|
| 1210 |
+
SSHCertPrivateKeyTypes = typing.Union[
|
| 1211 |
+
ec.EllipticCurvePrivateKey,
|
| 1212 |
+
rsa.RSAPrivateKey,
|
| 1213 |
+
ed25519.Ed25519PrivateKey,
|
| 1214 |
+
]
|
| 1215 |
+
|
| 1216 |
+
|
| 1217 |
+
# This is an undocumented limit enforced in the openssh codebase for sshd and
|
| 1218 |
+
# ssh-keygen, but it is undefined in the ssh certificates spec.
|
| 1219 |
+
_SSHKEY_CERT_MAX_PRINCIPALS = 256
|
| 1220 |
+
|
| 1221 |
+
|
| 1222 |
+
class SSHCertificateBuilder:
|
| 1223 |
+
def __init__(
|
| 1224 |
+
self,
|
| 1225 |
+
_public_key: SSHCertPublicKeyTypes | None = None,
|
| 1226 |
+
_serial: int | None = None,
|
| 1227 |
+
_type: SSHCertificateType | None = None,
|
| 1228 |
+
_key_id: bytes | None = None,
|
| 1229 |
+
_valid_principals: list[bytes] = [],
|
| 1230 |
+
_valid_for_all_principals: bool = False,
|
| 1231 |
+
_valid_before: int | None = None,
|
| 1232 |
+
_valid_after: int | None = None,
|
| 1233 |
+
_critical_options: list[tuple[bytes, bytes]] = [],
|
| 1234 |
+
_extensions: list[tuple[bytes, bytes]] = [],
|
| 1235 |
+
):
|
| 1236 |
+
self._public_key = _public_key
|
| 1237 |
+
self._serial = _serial
|
| 1238 |
+
self._type = _type
|
| 1239 |
+
self._key_id = _key_id
|
| 1240 |
+
self._valid_principals = _valid_principals
|
| 1241 |
+
self._valid_for_all_principals = _valid_for_all_principals
|
| 1242 |
+
self._valid_before = _valid_before
|
| 1243 |
+
self._valid_after = _valid_after
|
| 1244 |
+
self._critical_options = _critical_options
|
| 1245 |
+
self._extensions = _extensions
|
| 1246 |
+
|
| 1247 |
+
def public_key(
|
| 1248 |
+
self, public_key: SSHCertPublicKeyTypes
|
| 1249 |
+
) -> SSHCertificateBuilder:
|
| 1250 |
+
if not isinstance(
|
| 1251 |
+
public_key,
|
| 1252 |
+
(
|
| 1253 |
+
ec.EllipticCurvePublicKey,
|
| 1254 |
+
rsa.RSAPublicKey,
|
| 1255 |
+
ed25519.Ed25519PublicKey,
|
| 1256 |
+
),
|
| 1257 |
+
):
|
| 1258 |
+
raise TypeError("Unsupported key type")
|
| 1259 |
+
if self._public_key is not None:
|
| 1260 |
+
raise ValueError("public_key already set")
|
| 1261 |
+
|
| 1262 |
+
return SSHCertificateBuilder(
|
| 1263 |
+
_public_key=public_key,
|
| 1264 |
+
_serial=self._serial,
|
| 1265 |
+
_type=self._type,
|
| 1266 |
+
_key_id=self._key_id,
|
| 1267 |
+
_valid_principals=self._valid_principals,
|
| 1268 |
+
_valid_for_all_principals=self._valid_for_all_principals,
|
| 1269 |
+
_valid_before=self._valid_before,
|
| 1270 |
+
_valid_after=self._valid_after,
|
| 1271 |
+
_critical_options=self._critical_options,
|
| 1272 |
+
_extensions=self._extensions,
|
| 1273 |
+
)
|
| 1274 |
+
|
| 1275 |
+
def serial(self, serial: int) -> SSHCertificateBuilder:
|
| 1276 |
+
if not isinstance(serial, int):
|
| 1277 |
+
raise TypeError("serial must be an integer")
|
| 1278 |
+
if not 0 <= serial < 2**64:
|
| 1279 |
+
raise ValueError("serial must be between 0 and 2**64")
|
| 1280 |
+
if self._serial is not None:
|
| 1281 |
+
raise ValueError("serial already set")
|
| 1282 |
+
|
| 1283 |
+
return SSHCertificateBuilder(
|
| 1284 |
+
_public_key=self._public_key,
|
| 1285 |
+
_serial=serial,
|
| 1286 |
+
_type=self._type,
|
| 1287 |
+
_key_id=self._key_id,
|
| 1288 |
+
_valid_principals=self._valid_principals,
|
| 1289 |
+
_valid_for_all_principals=self._valid_for_all_principals,
|
| 1290 |
+
_valid_before=self._valid_before,
|
| 1291 |
+
_valid_after=self._valid_after,
|
| 1292 |
+
_critical_options=self._critical_options,
|
| 1293 |
+
_extensions=self._extensions,
|
| 1294 |
+
)
|
| 1295 |
+
|
| 1296 |
+
def type(self, type: SSHCertificateType) -> SSHCertificateBuilder:
|
| 1297 |
+
if not isinstance(type, SSHCertificateType):
|
| 1298 |
+
raise TypeError("type must be an SSHCertificateType")
|
| 1299 |
+
if self._type is not None:
|
| 1300 |
+
raise ValueError("type already set")
|
| 1301 |
+
|
| 1302 |
+
return SSHCertificateBuilder(
|
| 1303 |
+
_public_key=self._public_key,
|
| 1304 |
+
_serial=self._serial,
|
| 1305 |
+
_type=type,
|
| 1306 |
+
_key_id=self._key_id,
|
| 1307 |
+
_valid_principals=self._valid_principals,
|
| 1308 |
+
_valid_for_all_principals=self._valid_for_all_principals,
|
| 1309 |
+
_valid_before=self._valid_before,
|
| 1310 |
+
_valid_after=self._valid_after,
|
| 1311 |
+
_critical_options=self._critical_options,
|
| 1312 |
+
_extensions=self._extensions,
|
| 1313 |
+
)
|
| 1314 |
+
|
| 1315 |
+
def key_id(self, key_id: bytes) -> SSHCertificateBuilder:
|
| 1316 |
+
if not isinstance(key_id, bytes):
|
| 1317 |
+
raise TypeError("key_id must be bytes")
|
| 1318 |
+
if self._key_id is not None:
|
| 1319 |
+
raise ValueError("key_id already set")
|
| 1320 |
+
|
| 1321 |
+
return SSHCertificateBuilder(
|
| 1322 |
+
_public_key=self._public_key,
|
| 1323 |
+
_serial=self._serial,
|
| 1324 |
+
_type=self._type,
|
| 1325 |
+
_key_id=key_id,
|
| 1326 |
+
_valid_principals=self._valid_principals,
|
| 1327 |
+
_valid_for_all_principals=self._valid_for_all_principals,
|
| 1328 |
+
_valid_before=self._valid_before,
|
| 1329 |
+
_valid_after=self._valid_after,
|
| 1330 |
+
_critical_options=self._critical_options,
|
| 1331 |
+
_extensions=self._extensions,
|
| 1332 |
+
)
|
| 1333 |
+
|
| 1334 |
+
def valid_principals(
|
| 1335 |
+
self, valid_principals: list[bytes]
|
| 1336 |
+
) -> SSHCertificateBuilder:
|
| 1337 |
+
if self._valid_for_all_principals:
|
| 1338 |
+
raise ValueError(
|
| 1339 |
+
"Principals can't be set because the cert is valid "
|
| 1340 |
+
"for all principals"
|
| 1341 |
+
)
|
| 1342 |
+
if (
|
| 1343 |
+
not all(isinstance(x, bytes) for x in valid_principals)
|
| 1344 |
+
or not valid_principals
|
| 1345 |
+
):
|
| 1346 |
+
raise TypeError(
|
| 1347 |
+
"principals must be a list of bytes and can't be empty"
|
| 1348 |
+
)
|
| 1349 |
+
if self._valid_principals:
|
| 1350 |
+
raise ValueError("valid_principals already set")
|
| 1351 |
+
|
| 1352 |
+
if len(valid_principals) > _SSHKEY_CERT_MAX_PRINCIPALS:
|
| 1353 |
+
raise ValueError(
|
| 1354 |
+
"Reached or exceeded the maximum number of valid_principals"
|
| 1355 |
+
)
|
| 1356 |
+
|
| 1357 |
+
return SSHCertificateBuilder(
|
| 1358 |
+
_public_key=self._public_key,
|
| 1359 |
+
_serial=self._serial,
|
| 1360 |
+
_type=self._type,
|
| 1361 |
+
_key_id=self._key_id,
|
| 1362 |
+
_valid_principals=valid_principals,
|
| 1363 |
+
_valid_for_all_principals=self._valid_for_all_principals,
|
| 1364 |
+
_valid_before=self._valid_before,
|
| 1365 |
+
_valid_after=self._valid_after,
|
| 1366 |
+
_critical_options=self._critical_options,
|
| 1367 |
+
_extensions=self._extensions,
|
| 1368 |
+
)
|
| 1369 |
+
|
| 1370 |
+
def valid_for_all_principals(self):
|
| 1371 |
+
if self._valid_principals:
|
| 1372 |
+
raise ValueError(
|
| 1373 |
+
"valid_principals already set, can't set "
|
| 1374 |
+
"valid_for_all_principals"
|
| 1375 |
+
)
|
| 1376 |
+
if self._valid_for_all_principals:
|
| 1377 |
+
raise ValueError("valid_for_all_principals already set")
|
| 1378 |
+
|
| 1379 |
+
return SSHCertificateBuilder(
|
| 1380 |
+
_public_key=self._public_key,
|
| 1381 |
+
_serial=self._serial,
|
| 1382 |
+
_type=self._type,
|
| 1383 |
+
_key_id=self._key_id,
|
| 1384 |
+
_valid_principals=self._valid_principals,
|
| 1385 |
+
_valid_for_all_principals=True,
|
| 1386 |
+
_valid_before=self._valid_before,
|
| 1387 |
+
_valid_after=self._valid_after,
|
| 1388 |
+
_critical_options=self._critical_options,
|
| 1389 |
+
_extensions=self._extensions,
|
| 1390 |
+
)
|
| 1391 |
+
|
| 1392 |
+
def valid_before(self, valid_before: int | float) -> SSHCertificateBuilder:
|
| 1393 |
+
if not isinstance(valid_before, (int, float)):
|
| 1394 |
+
raise TypeError("valid_before must be an int or float")
|
| 1395 |
+
valid_before = int(valid_before)
|
| 1396 |
+
if valid_before < 0 or valid_before >= 2**64:
|
| 1397 |
+
raise ValueError("valid_before must [0, 2**64)")
|
| 1398 |
+
if self._valid_before is not None:
|
| 1399 |
+
raise ValueError("valid_before already set")
|
| 1400 |
+
|
| 1401 |
+
return SSHCertificateBuilder(
|
| 1402 |
+
_public_key=self._public_key,
|
| 1403 |
+
_serial=self._serial,
|
| 1404 |
+
_type=self._type,
|
| 1405 |
+
_key_id=self._key_id,
|
| 1406 |
+
_valid_principals=self._valid_principals,
|
| 1407 |
+
_valid_for_all_principals=self._valid_for_all_principals,
|
| 1408 |
+
_valid_before=valid_before,
|
| 1409 |
+
_valid_after=self._valid_after,
|
| 1410 |
+
_critical_options=self._critical_options,
|
| 1411 |
+
_extensions=self._extensions,
|
| 1412 |
+
)
|
| 1413 |
+
|
| 1414 |
+
def valid_after(self, valid_after: int | float) -> SSHCertificateBuilder:
|
| 1415 |
+
if not isinstance(valid_after, (int, float)):
|
| 1416 |
+
raise TypeError("valid_after must be an int or float")
|
| 1417 |
+
valid_after = int(valid_after)
|
| 1418 |
+
if valid_after < 0 or valid_after >= 2**64:
|
| 1419 |
+
raise ValueError("valid_after must [0, 2**64)")
|
| 1420 |
+
if self._valid_after is not None:
|
| 1421 |
+
raise ValueError("valid_after already set")
|
| 1422 |
+
|
| 1423 |
+
return SSHCertificateBuilder(
|
| 1424 |
+
_public_key=self._public_key,
|
| 1425 |
+
_serial=self._serial,
|
| 1426 |
+
_type=self._type,
|
| 1427 |
+
_key_id=self._key_id,
|
| 1428 |
+
_valid_principals=self._valid_principals,
|
| 1429 |
+
_valid_for_all_principals=self._valid_for_all_principals,
|
| 1430 |
+
_valid_before=self._valid_before,
|
| 1431 |
+
_valid_after=valid_after,
|
| 1432 |
+
_critical_options=self._critical_options,
|
| 1433 |
+
_extensions=self._extensions,
|
| 1434 |
+
)
|
| 1435 |
+
|
| 1436 |
+
def add_critical_option(
|
| 1437 |
+
self, name: bytes, value: bytes
|
| 1438 |
+
) -> SSHCertificateBuilder:
|
| 1439 |
+
if not isinstance(name, bytes) or not isinstance(value, bytes):
|
| 1440 |
+
raise TypeError("name and value must be bytes")
|
| 1441 |
+
# This is O(n**2)
|
| 1442 |
+
if name in [name for name, _ in self._critical_options]:
|
| 1443 |
+
raise ValueError("Duplicate critical option name")
|
| 1444 |
+
|
| 1445 |
+
return SSHCertificateBuilder(
|
| 1446 |
+
_public_key=self._public_key,
|
| 1447 |
+
_serial=self._serial,
|
| 1448 |
+
_type=self._type,
|
| 1449 |
+
_key_id=self._key_id,
|
| 1450 |
+
_valid_principals=self._valid_principals,
|
| 1451 |
+
_valid_for_all_principals=self._valid_for_all_principals,
|
| 1452 |
+
_valid_before=self._valid_before,
|
| 1453 |
+
_valid_after=self._valid_after,
|
| 1454 |
+
_critical_options=[*self._critical_options, (name, value)],
|
| 1455 |
+
_extensions=self._extensions,
|
| 1456 |
+
)
|
| 1457 |
+
|
| 1458 |
+
def add_extension(
|
| 1459 |
+
self, name: bytes, value: bytes
|
| 1460 |
+
) -> SSHCertificateBuilder:
|
| 1461 |
+
if not isinstance(name, bytes) or not isinstance(value, bytes):
|
| 1462 |
+
raise TypeError("name and value must be bytes")
|
| 1463 |
+
# This is O(n**2)
|
| 1464 |
+
if name in [name for name, _ in self._extensions]:
|
| 1465 |
+
raise ValueError("Duplicate extension name")
|
| 1466 |
+
|
| 1467 |
+
return SSHCertificateBuilder(
|
| 1468 |
+
_public_key=self._public_key,
|
| 1469 |
+
_serial=self._serial,
|
| 1470 |
+
_type=self._type,
|
| 1471 |
+
_key_id=self._key_id,
|
| 1472 |
+
_valid_principals=self._valid_principals,
|
| 1473 |
+
_valid_for_all_principals=self._valid_for_all_principals,
|
| 1474 |
+
_valid_before=self._valid_before,
|
| 1475 |
+
_valid_after=self._valid_after,
|
| 1476 |
+
_critical_options=self._critical_options,
|
| 1477 |
+
_extensions=[*self._extensions, (name, value)],
|
| 1478 |
+
)
|
| 1479 |
+
|
| 1480 |
+
def sign(self, private_key: SSHCertPrivateKeyTypes) -> SSHCertificate:
|
| 1481 |
+
if not isinstance(
|
| 1482 |
+
private_key,
|
| 1483 |
+
(
|
| 1484 |
+
ec.EllipticCurvePrivateKey,
|
| 1485 |
+
rsa.RSAPrivateKey,
|
| 1486 |
+
ed25519.Ed25519PrivateKey,
|
| 1487 |
+
),
|
| 1488 |
+
):
|
| 1489 |
+
raise TypeError("Unsupported private key type")
|
| 1490 |
+
|
| 1491 |
+
if self._public_key is None:
|
| 1492 |
+
raise ValueError("public_key must be set")
|
| 1493 |
+
|
| 1494 |
+
# Not required
|
| 1495 |
+
serial = 0 if self._serial is None else self._serial
|
| 1496 |
+
|
| 1497 |
+
if self._type is None:
|
| 1498 |
+
raise ValueError("type must be set")
|
| 1499 |
+
|
| 1500 |
+
# Not required
|
| 1501 |
+
key_id = b"" if self._key_id is None else self._key_id
|
| 1502 |
+
|
| 1503 |
+
# A zero length list is valid, but means the certificate
|
| 1504 |
+
# is valid for any principal of the specified type. We require
|
| 1505 |
+
# the user to explicitly set valid_for_all_principals to get
|
| 1506 |
+
# that behavior.
|
| 1507 |
+
if not self._valid_principals and not self._valid_for_all_principals:
|
| 1508 |
+
raise ValueError(
|
| 1509 |
+
"valid_principals must be set if valid_for_all_principals "
|
| 1510 |
+
"is False"
|
| 1511 |
+
)
|
| 1512 |
+
|
| 1513 |
+
if self._valid_before is None:
|
| 1514 |
+
raise ValueError("valid_before must be set")
|
| 1515 |
+
|
| 1516 |
+
if self._valid_after is None:
|
| 1517 |
+
raise ValueError("valid_after must be set")
|
| 1518 |
+
|
| 1519 |
+
if self._valid_after > self._valid_before:
|
| 1520 |
+
raise ValueError("valid_after must be earlier than valid_before")
|
| 1521 |
+
|
| 1522 |
+
# lexically sort our byte strings
|
| 1523 |
+
self._critical_options.sort(key=lambda x: x[0])
|
| 1524 |
+
self._extensions.sort(key=lambda x: x[0])
|
| 1525 |
+
|
| 1526 |
+
key_type = _get_ssh_key_type(self._public_key)
|
| 1527 |
+
cert_prefix = key_type + _CERT_SUFFIX
|
| 1528 |
+
|
| 1529 |
+
# Marshal the bytes to be signed
|
| 1530 |
+
nonce = os.urandom(32)
|
| 1531 |
+
kformat = _lookup_kformat(key_type)
|
| 1532 |
+
f = _FragList()
|
| 1533 |
+
f.put_sshstr(cert_prefix)
|
| 1534 |
+
f.put_sshstr(nonce)
|
| 1535 |
+
kformat.encode_public(self._public_key, f)
|
| 1536 |
+
f.put_u64(serial)
|
| 1537 |
+
f.put_u32(self._type.value)
|
| 1538 |
+
f.put_sshstr(key_id)
|
| 1539 |
+
fprincipals = _FragList()
|
| 1540 |
+
for p in self._valid_principals:
|
| 1541 |
+
fprincipals.put_sshstr(p)
|
| 1542 |
+
f.put_sshstr(fprincipals.tobytes())
|
| 1543 |
+
f.put_u64(self._valid_after)
|
| 1544 |
+
f.put_u64(self._valid_before)
|
| 1545 |
+
fcrit = _FragList()
|
| 1546 |
+
for name, value in self._critical_options:
|
| 1547 |
+
fcrit.put_sshstr(name)
|
| 1548 |
+
if len(value) > 0:
|
| 1549 |
+
foptval = _FragList()
|
| 1550 |
+
foptval.put_sshstr(value)
|
| 1551 |
+
fcrit.put_sshstr(foptval.tobytes())
|
| 1552 |
+
else:
|
| 1553 |
+
fcrit.put_sshstr(value)
|
| 1554 |
+
f.put_sshstr(fcrit.tobytes())
|
| 1555 |
+
fext = _FragList()
|
| 1556 |
+
for name, value in self._extensions:
|
| 1557 |
+
fext.put_sshstr(name)
|
| 1558 |
+
if len(value) > 0:
|
| 1559 |
+
fextval = _FragList()
|
| 1560 |
+
fextval.put_sshstr(value)
|
| 1561 |
+
fext.put_sshstr(fextval.tobytes())
|
| 1562 |
+
else:
|
| 1563 |
+
fext.put_sshstr(value)
|
| 1564 |
+
f.put_sshstr(fext.tobytes())
|
| 1565 |
+
f.put_sshstr(b"") # RESERVED FIELD
|
| 1566 |
+
# encode CA public key
|
| 1567 |
+
ca_type = _get_ssh_key_type(private_key)
|
| 1568 |
+
caformat = _lookup_kformat(ca_type)
|
| 1569 |
+
caf = _FragList()
|
| 1570 |
+
caf.put_sshstr(ca_type)
|
| 1571 |
+
caformat.encode_public(private_key.public_key(), caf)
|
| 1572 |
+
f.put_sshstr(caf.tobytes())
|
| 1573 |
+
# Sigs according to the rules defined for the CA's public key
|
| 1574 |
+
# (RFC4253 section 6.6 for ssh-rsa, RFC5656 for ECDSA,
|
| 1575 |
+
# and RFC8032 for Ed25519).
|
| 1576 |
+
if isinstance(private_key, ed25519.Ed25519PrivateKey):
|
| 1577 |
+
signature = private_key.sign(f.tobytes())
|
| 1578 |
+
fsig = _FragList()
|
| 1579 |
+
fsig.put_sshstr(ca_type)
|
| 1580 |
+
fsig.put_sshstr(signature)
|
| 1581 |
+
f.put_sshstr(fsig.tobytes())
|
| 1582 |
+
elif isinstance(private_key, ec.EllipticCurvePrivateKey):
|
| 1583 |
+
hash_alg = _get_ec_hash_alg(private_key.curve)
|
| 1584 |
+
signature = private_key.sign(f.tobytes(), ec.ECDSA(hash_alg))
|
| 1585 |
+
r, s = asym_utils.decode_dss_signature(signature)
|
| 1586 |
+
fsig = _FragList()
|
| 1587 |
+
fsig.put_sshstr(ca_type)
|
| 1588 |
+
fsigblob = _FragList()
|
| 1589 |
+
fsigblob.put_mpint(r)
|
| 1590 |
+
fsigblob.put_mpint(s)
|
| 1591 |
+
fsig.put_sshstr(fsigblob.tobytes())
|
| 1592 |
+
f.put_sshstr(fsig.tobytes())
|
| 1593 |
+
|
| 1594 |
+
else:
|
| 1595 |
+
assert isinstance(private_key, rsa.RSAPrivateKey)
|
| 1596 |
+
# Just like Golang, we're going to use SHA512 for RSA
|
| 1597 |
+
# https://cs.opensource.google/go/x/crypto/+/refs/tags/
|
| 1598 |
+
# v0.4.0:ssh/certs.go;l=445
|
| 1599 |
+
# RFC 8332 defines SHA256 and 512 as options
|
| 1600 |
+
fsig = _FragList()
|
| 1601 |
+
fsig.put_sshstr(_SSH_RSA_SHA512)
|
| 1602 |
+
signature = private_key.sign(
|
| 1603 |
+
f.tobytes(), padding.PKCS1v15(), hashes.SHA512()
|
| 1604 |
+
)
|
| 1605 |
+
fsig.put_sshstr(signature)
|
| 1606 |
+
f.put_sshstr(fsig.tobytes())
|
| 1607 |
+
|
| 1608 |
+
cert_data = binascii.b2a_base64(f.tobytes()).strip()
|
| 1609 |
+
# load_ssh_public_identity returns a union, but this is
|
| 1610 |
+
# guaranteed to be an SSHCertificate, so we cast to make
|
| 1611 |
+
# mypy happy.
|
| 1612 |
+
return typing.cast(
|
| 1613 |
+
SSHCertificate,
|
| 1614 |
+
load_ssh_public_identity(b"".join([cert_prefix, b" ", cert_data])),
|
| 1615 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class InvalidToken(Exception):
|
| 9 |
+
pass
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import base64
|
| 8 |
+
import typing
|
| 9 |
+
from urllib.parse import quote, urlencode
|
| 10 |
+
|
| 11 |
+
from cryptography.hazmat.primitives import constant_time, hmac
|
| 12 |
+
from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512
|
| 13 |
+
from cryptography.hazmat.primitives.twofactor import InvalidToken
|
| 14 |
+
from cryptography.utils import Buffer
|
| 15 |
+
|
| 16 |
+
HOTPHashTypes = typing.Union[SHA1, SHA256, SHA512]
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _generate_uri(
|
| 20 |
+
hotp: HOTP,
|
| 21 |
+
type_name: str,
|
| 22 |
+
account_name: str,
|
| 23 |
+
issuer: str | None,
|
| 24 |
+
extra_parameters: list[tuple[str, int]],
|
| 25 |
+
) -> str:
|
| 26 |
+
parameters = [
|
| 27 |
+
("digits", hotp._length),
|
| 28 |
+
("secret", base64.b32encode(hotp._key)),
|
| 29 |
+
("algorithm", hotp._algorithm.name.upper()),
|
| 30 |
+
]
|
| 31 |
+
|
| 32 |
+
if issuer is not None:
|
| 33 |
+
parameters.append(("issuer", issuer))
|
| 34 |
+
|
| 35 |
+
parameters.extend(extra_parameters)
|
| 36 |
+
|
| 37 |
+
label = (
|
| 38 |
+
f"{quote(issuer)}:{quote(account_name)}"
|
| 39 |
+
if issuer
|
| 40 |
+
else quote(account_name)
|
| 41 |
+
)
|
| 42 |
+
return f"otpauth://{type_name}/{label}?{urlencode(parameters)}"
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class HOTP:
|
| 46 |
+
def __init__(
|
| 47 |
+
self,
|
| 48 |
+
key: Buffer,
|
| 49 |
+
length: int,
|
| 50 |
+
algorithm: HOTPHashTypes,
|
| 51 |
+
backend: typing.Any = None,
|
| 52 |
+
enforce_key_length: bool = True,
|
| 53 |
+
) -> None:
|
| 54 |
+
if len(key) < 16 and enforce_key_length is True:
|
| 55 |
+
raise ValueError("Key length has to be at least 128 bits.")
|
| 56 |
+
|
| 57 |
+
if not isinstance(length, int):
|
| 58 |
+
raise TypeError("Length parameter must be an integer type.")
|
| 59 |
+
|
| 60 |
+
if length < 6 or length > 8:
|
| 61 |
+
raise ValueError("Length of HOTP has to be between 6 and 8.")
|
| 62 |
+
|
| 63 |
+
if not isinstance(algorithm, (SHA1, SHA256, SHA512)):
|
| 64 |
+
raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.")
|
| 65 |
+
|
| 66 |
+
self._key = key
|
| 67 |
+
self._length = length
|
| 68 |
+
self._algorithm = algorithm
|
| 69 |
+
|
| 70 |
+
def generate(self, counter: int) -> bytes:
|
| 71 |
+
if not isinstance(counter, int):
|
| 72 |
+
raise TypeError("Counter parameter must be an integer type.")
|
| 73 |
+
|
| 74 |
+
truncated_value = self._dynamic_truncate(counter)
|
| 75 |
+
hotp = truncated_value % (10**self._length)
|
| 76 |
+
return "{0:0{1}}".format(hotp, self._length).encode()
|
| 77 |
+
|
| 78 |
+
def verify(self, hotp: bytes, counter: int) -> None:
|
| 79 |
+
if not constant_time.bytes_eq(self.generate(counter), hotp):
|
| 80 |
+
raise InvalidToken("Supplied HOTP value does not match.")
|
| 81 |
+
|
| 82 |
+
def _dynamic_truncate(self, counter: int) -> int:
|
| 83 |
+
ctx = hmac.HMAC(self._key, self._algorithm)
|
| 84 |
+
|
| 85 |
+
try:
|
| 86 |
+
ctx.update(counter.to_bytes(length=8, byteorder="big"))
|
| 87 |
+
except OverflowError:
|
| 88 |
+
raise ValueError(f"Counter must be between 0 and {2**64 - 1}.")
|
| 89 |
+
|
| 90 |
+
hmac_value = ctx.finalize()
|
| 91 |
+
|
| 92 |
+
offset = hmac_value[len(hmac_value) - 1] & 0b1111
|
| 93 |
+
p = hmac_value[offset : offset + 4]
|
| 94 |
+
return int.from_bytes(p, byteorder="big") & 0x7FFFFFFF
|
| 95 |
+
|
| 96 |
+
def get_provisioning_uri(
|
| 97 |
+
self, account_name: str, counter: int, issuer: str | None
|
| 98 |
+
) -> str:
|
| 99 |
+
return _generate_uri(
|
| 100 |
+
self, "hotp", account_name, issuer, [("counter", int(counter))]
|
| 101 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/totp.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import typing
|
| 8 |
+
|
| 9 |
+
from cryptography.hazmat.primitives import constant_time
|
| 10 |
+
from cryptography.hazmat.primitives.twofactor import InvalidToken
|
| 11 |
+
from cryptography.hazmat.primitives.twofactor.hotp import (
|
| 12 |
+
HOTP,
|
| 13 |
+
HOTPHashTypes,
|
| 14 |
+
_generate_uri,
|
| 15 |
+
)
|
| 16 |
+
from cryptography.utils import Buffer
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class TOTP:
|
| 20 |
+
def __init__(
|
| 21 |
+
self,
|
| 22 |
+
key: Buffer,
|
| 23 |
+
length: int,
|
| 24 |
+
algorithm: HOTPHashTypes,
|
| 25 |
+
time_step: int,
|
| 26 |
+
backend: typing.Any = None,
|
| 27 |
+
enforce_key_length: bool = True,
|
| 28 |
+
):
|
| 29 |
+
self._time_step = time_step
|
| 30 |
+
self._hotp = HOTP(
|
| 31 |
+
key, length, algorithm, enforce_key_length=enforce_key_length
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
def generate(self, time: int | float) -> bytes:
|
| 35 |
+
if not isinstance(time, (int, float)):
|
| 36 |
+
raise TypeError(
|
| 37 |
+
"Time parameter must be an integer type or float type."
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
counter = int(time / self._time_step)
|
| 41 |
+
return self._hotp.generate(counter)
|
| 42 |
+
|
| 43 |
+
def verify(self, totp: bytes, time: int) -> None:
|
| 44 |
+
if not constant_time.bytes_eq(self.generate(time), totp):
|
| 45 |
+
raise InvalidToken("Supplied TOTP value does not match.")
|
| 46 |
+
|
| 47 |
+
def get_provisioning_uri(
|
| 48 |
+
self, account_name: str, issuer: str | None
|
| 49 |
+
) -> str:
|
| 50 |
+
return _generate_uri(
|
| 51 |
+
self._hotp,
|
| 52 |
+
"totp",
|
| 53 |
+
account_name,
|
| 54 |
+
issuer,
|
| 55 |
+
[("period", int(self._time_step))],
|
| 56 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/py.typed
ADDED
|
File without changes
|
.mcp-venv/lib/python3.13/site-packages/cryptography/utils.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import enum
|
| 8 |
+
import sys
|
| 9 |
+
import types
|
| 10 |
+
import typing
|
| 11 |
+
import warnings
|
| 12 |
+
from collections.abc import Sequence
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
# We use a UserWarning subclass, instead of DeprecationWarning, because CPython
|
| 16 |
+
# decided deprecation warnings should be invisible by default.
|
| 17 |
+
class CryptographyDeprecationWarning(UserWarning):
|
| 18 |
+
pass
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# Several APIs were deprecated with no specific end-of-life date because of the
|
| 22 |
+
# ubiquity of their use. They should not be removed until we agree on when that
|
| 23 |
+
# cycle ends.
|
| 24 |
+
DeprecatedIn36 = CryptographyDeprecationWarning
|
| 25 |
+
DeprecatedIn40 = CryptographyDeprecationWarning
|
| 26 |
+
DeprecatedIn41 = CryptographyDeprecationWarning
|
| 27 |
+
DeprecatedIn42 = CryptographyDeprecationWarning
|
| 28 |
+
DeprecatedIn43 = CryptographyDeprecationWarning
|
| 29 |
+
DeprecatedIn47 = CryptographyDeprecationWarning
|
| 30 |
+
DeprecatedIn50 = CryptographyDeprecationWarning
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
# If you're wondering why we don't use `Buffer`, it's because `Buffer` would
|
| 34 |
+
# be more accurately named: Bufferable. It means something which has an
|
| 35 |
+
# `__buffer__`. Which means you can't actually treat the result as a buffer
|
| 36 |
+
# (and do things like take a `len()`).
|
| 37 |
+
Buffer = typing.Union[bytes, bytearray, memoryview]
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _check_bytes(name: str, value: bytes) -> None:
|
| 41 |
+
if not isinstance(value, bytes):
|
| 42 |
+
raise TypeError(f"{name} must be bytes")
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def _check_byteslike(name: str, value: Buffer) -> None:
|
| 46 |
+
if isinstance(value, (bytes, bytearray, memoryview)):
|
| 47 |
+
return
|
| 48 |
+
try:
|
| 49 |
+
memoryview(value)
|
| 50 |
+
except TypeError:
|
| 51 |
+
raise TypeError(f"{name} must be bytes-like")
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def int_to_bytes(integer: int, length: int | None = None) -> bytes:
|
| 55 |
+
if length == 0:
|
| 56 |
+
raise ValueError("length argument can't be 0")
|
| 57 |
+
return integer.to_bytes(
|
| 58 |
+
length or (integer.bit_length() + 7) // 8 or 1, "big"
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class InterfaceNotImplemented(Exception):
|
| 63 |
+
pass
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class _DeprecatedValue:
|
| 67 |
+
def __init__(self, value: object, message: str, warning_class):
|
| 68 |
+
self.value = value
|
| 69 |
+
self.message = message
|
| 70 |
+
self.warning_class = warning_class
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
class _ModuleWithDeprecations(types.ModuleType):
|
| 74 |
+
def __init__(self, module: types.ModuleType):
|
| 75 |
+
super().__init__(module.__name__)
|
| 76 |
+
self.__dict__["_module"] = module
|
| 77 |
+
|
| 78 |
+
def __getattr__(self, name: str) -> typing.Any:
|
| 79 |
+
obj = getattr(self._module, name)
|
| 80 |
+
if isinstance(obj, _DeprecatedValue):
|
| 81 |
+
warnings.warn(obj.message, obj.warning_class, stacklevel=2)
|
| 82 |
+
obj = obj.value
|
| 83 |
+
else:
|
| 84 |
+
# Cache non-deprecated attributes in our own `__dict__` so that
|
| 85 |
+
# subsequent lookups are ordinary module attribute accesses and
|
| 86 |
+
# don't pay for this `__getattr__` (which would otherwise defeat
|
| 87 |
+
# CPython's LOAD_ATTR module caching for every attribute of the
|
| 88 |
+
# module). `__setattr__` and `__delattr__` keep the cache
|
| 89 |
+
# coherent.
|
| 90 |
+
self.__dict__[name] = obj
|
| 91 |
+
return obj
|
| 92 |
+
|
| 93 |
+
def __setattr__(self, attr: str, value: object) -> None:
|
| 94 |
+
if isinstance(value, _DeprecatedValue):
|
| 95 |
+
self.__dict__.pop(attr, None)
|
| 96 |
+
else:
|
| 97 |
+
self.__dict__[attr] = value
|
| 98 |
+
setattr(self._module, attr, value)
|
| 99 |
+
|
| 100 |
+
def __delattr__(self, attr: str) -> None:
|
| 101 |
+
obj = getattr(self._module, attr)
|
| 102 |
+
if isinstance(obj, _DeprecatedValue):
|
| 103 |
+
warnings.warn(obj.message, obj.warning_class, stacklevel=2)
|
| 104 |
+
|
| 105 |
+
self.__dict__.pop(attr, None)
|
| 106 |
+
delattr(self._module, attr)
|
| 107 |
+
|
| 108 |
+
def __dir__(self) -> Sequence[str]:
|
| 109 |
+
return ["_module", *dir(self._module)]
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def deprecated(
|
| 113 |
+
value: object,
|
| 114 |
+
module_name: str,
|
| 115 |
+
message: str,
|
| 116 |
+
warning_class: type[Warning],
|
| 117 |
+
name: str | None = None,
|
| 118 |
+
) -> _DeprecatedValue:
|
| 119 |
+
module = sys.modules[module_name]
|
| 120 |
+
if not isinstance(module, _ModuleWithDeprecations):
|
| 121 |
+
sys.modules[module_name] = module = _ModuleWithDeprecations(module)
|
| 122 |
+
dv = _DeprecatedValue(value, message, warning_class)
|
| 123 |
+
# Maintain backwards compatibility with `name is None` for pyOpenSSL.
|
| 124 |
+
if name is not None:
|
| 125 |
+
setattr(module, name, dv)
|
| 126 |
+
return dv
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
# Python 3.10 changed representation of enums. We use well-defined object
|
| 130 |
+
# representation and string representation from Python 3.9.
|
| 131 |
+
class Enum(enum.Enum):
|
| 132 |
+
def __repr__(self) -> str:
|
| 133 |
+
return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>"
|
| 134 |
+
|
| 135 |
+
def __str__(self) -> str:
|
| 136 |
+
return f"{self.__class__.__name__}.{self._name_}"
|
.mcp-venv/lib/python3.13/site-packages/cryptography/x509/__init__.py
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.x509 import certificate_transparency, oid, verification
|
| 8 |
+
from cryptography.x509.base import (
|
| 9 |
+
Attribute,
|
| 10 |
+
AttributeNotFound,
|
| 11 |
+
Attributes,
|
| 12 |
+
Certificate,
|
| 13 |
+
CertificateBuilder,
|
| 14 |
+
CertificateRevocationList,
|
| 15 |
+
CertificateRevocationListBuilder,
|
| 16 |
+
CertificateSigningRequest,
|
| 17 |
+
CertificateSigningRequestBuilder,
|
| 18 |
+
InvalidVersion,
|
| 19 |
+
RevokedCertificate,
|
| 20 |
+
RevokedCertificateBuilder,
|
| 21 |
+
Version,
|
| 22 |
+
load_der_x509_certificate,
|
| 23 |
+
load_der_x509_crl,
|
| 24 |
+
load_der_x509_csr,
|
| 25 |
+
load_pem_x509_certificate,
|
| 26 |
+
load_pem_x509_certificates,
|
| 27 |
+
load_pem_x509_crl,
|
| 28 |
+
load_pem_x509_csr,
|
| 29 |
+
random_serial_number,
|
| 30 |
+
)
|
| 31 |
+
from cryptography.x509.extensions import (
|
| 32 |
+
AccessDescription,
|
| 33 |
+
Admission,
|
| 34 |
+
Admissions,
|
| 35 |
+
AuthorityInformationAccess,
|
| 36 |
+
AuthorityKeyIdentifier,
|
| 37 |
+
BasicConstraints,
|
| 38 |
+
CertificateIssuer,
|
| 39 |
+
CertificatePolicies,
|
| 40 |
+
CRLDistributionPoints,
|
| 41 |
+
CRLNumber,
|
| 42 |
+
CRLReason,
|
| 43 |
+
DeltaCRLIndicator,
|
| 44 |
+
DistributionPoint,
|
| 45 |
+
DuplicateExtension,
|
| 46 |
+
ExtendedKeyUsage,
|
| 47 |
+
Extension,
|
| 48 |
+
ExtensionNotFound,
|
| 49 |
+
Extensions,
|
| 50 |
+
ExtensionType,
|
| 51 |
+
FreshestCRL,
|
| 52 |
+
GeneralNames,
|
| 53 |
+
InhibitAnyPolicy,
|
| 54 |
+
InvalidityDate,
|
| 55 |
+
IssuerAlternativeName,
|
| 56 |
+
IssuingDistributionPoint,
|
| 57 |
+
KeyUsage,
|
| 58 |
+
MSCertificateTemplate,
|
| 59 |
+
NameConstraints,
|
| 60 |
+
NamingAuthority,
|
| 61 |
+
NoticeReference,
|
| 62 |
+
OCSPAcceptableResponses,
|
| 63 |
+
OCSPNoCheck,
|
| 64 |
+
OCSPNonce,
|
| 65 |
+
PolicyConstraints,
|
| 66 |
+
PolicyInformation,
|
| 67 |
+
PrecertificateSignedCertificateTimestamps,
|
| 68 |
+
PrecertPoison,
|
| 69 |
+
PrivateKeyUsagePeriod,
|
| 70 |
+
ProfessionInfo,
|
| 71 |
+
ReasonFlags,
|
| 72 |
+
SignedCertificateTimestamps,
|
| 73 |
+
SubjectAlternativeName,
|
| 74 |
+
SubjectInformationAccess,
|
| 75 |
+
SubjectKeyIdentifier,
|
| 76 |
+
TLSFeature,
|
| 77 |
+
TLSFeatureType,
|
| 78 |
+
UnrecognizedExtension,
|
| 79 |
+
UserNotice,
|
| 80 |
+
)
|
| 81 |
+
from cryptography.x509.general_name import (
|
| 82 |
+
DirectoryName,
|
| 83 |
+
DNSName,
|
| 84 |
+
GeneralName,
|
| 85 |
+
IPAddress,
|
| 86 |
+
OtherName,
|
| 87 |
+
RegisteredID,
|
| 88 |
+
RFC822Name,
|
| 89 |
+
UniformResourceIdentifier,
|
| 90 |
+
UnsupportedGeneralNameType,
|
| 91 |
+
)
|
| 92 |
+
from cryptography.x509.name import (
|
| 93 |
+
Name,
|
| 94 |
+
NameAttribute,
|
| 95 |
+
RelativeDistinguishedName,
|
| 96 |
+
)
|
| 97 |
+
from cryptography.x509.oid import (
|
| 98 |
+
AuthorityInformationAccessOID,
|
| 99 |
+
CertificatePoliciesOID,
|
| 100 |
+
CRLEntryExtensionOID,
|
| 101 |
+
ExtendedKeyUsageOID,
|
| 102 |
+
ExtensionOID,
|
| 103 |
+
NameOID,
|
| 104 |
+
ObjectIdentifier,
|
| 105 |
+
PublicKeyAlgorithmOID,
|
| 106 |
+
SignatureAlgorithmOID,
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS
|
| 110 |
+
OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER
|
| 111 |
+
OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS
|
| 112 |
+
OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES
|
| 113 |
+
OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS
|
| 114 |
+
OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE
|
| 115 |
+
OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL
|
| 116 |
+
OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY
|
| 117 |
+
OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME
|
| 118 |
+
OID_KEY_USAGE = ExtensionOID.KEY_USAGE
|
| 119 |
+
OID_PRIVATE_KEY_USAGE_PERIOD = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD
|
| 120 |
+
OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS
|
| 121 |
+
OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK
|
| 122 |
+
OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS
|
| 123 |
+
OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS
|
| 124 |
+
OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME
|
| 125 |
+
OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES
|
| 126 |
+
OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS
|
| 127 |
+
OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER
|
| 128 |
+
|
| 129 |
+
OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1
|
| 130 |
+
OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224
|
| 131 |
+
OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256
|
| 132 |
+
OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1
|
| 133 |
+
OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224
|
| 134 |
+
OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256
|
| 135 |
+
OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384
|
| 136 |
+
OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512
|
| 137 |
+
OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5
|
| 138 |
+
OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1
|
| 139 |
+
OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224
|
| 140 |
+
OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256
|
| 141 |
+
OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384
|
| 142 |
+
OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512
|
| 143 |
+
OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS
|
| 144 |
+
|
| 145 |
+
OID_COMMON_NAME = NameOID.COMMON_NAME
|
| 146 |
+
OID_COUNTRY_NAME = NameOID.COUNTRY_NAME
|
| 147 |
+
OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT
|
| 148 |
+
OID_DN_QUALIFIER = NameOID.DN_QUALIFIER
|
| 149 |
+
OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS
|
| 150 |
+
OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER
|
| 151 |
+
OID_GIVEN_NAME = NameOID.GIVEN_NAME
|
| 152 |
+
OID_LOCALITY_NAME = NameOID.LOCALITY_NAME
|
| 153 |
+
OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME
|
| 154 |
+
OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME
|
| 155 |
+
OID_PSEUDONYM = NameOID.PSEUDONYM
|
| 156 |
+
OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER
|
| 157 |
+
OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME
|
| 158 |
+
OID_SURNAME = NameOID.SURNAME
|
| 159 |
+
OID_TITLE = NameOID.TITLE
|
| 160 |
+
|
| 161 |
+
OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH
|
| 162 |
+
OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING
|
| 163 |
+
OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION
|
| 164 |
+
OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING
|
| 165 |
+
OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH
|
| 166 |
+
OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING
|
| 167 |
+
|
| 168 |
+
OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY
|
| 169 |
+
OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER
|
| 170 |
+
OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE
|
| 171 |
+
|
| 172 |
+
OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER
|
| 173 |
+
OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON
|
| 174 |
+
OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE
|
| 175 |
+
|
| 176 |
+
OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS
|
| 177 |
+
OID_OCSP = AuthorityInformationAccessOID.OCSP
|
| 178 |
+
|
| 179 |
+
__all__ = [
|
| 180 |
+
"OID_CA_ISSUERS",
|
| 181 |
+
"OID_OCSP",
|
| 182 |
+
"AccessDescription",
|
| 183 |
+
"Admission",
|
| 184 |
+
"Admissions",
|
| 185 |
+
"Attribute",
|
| 186 |
+
"AttributeNotFound",
|
| 187 |
+
"Attributes",
|
| 188 |
+
"AuthorityInformationAccess",
|
| 189 |
+
"AuthorityKeyIdentifier",
|
| 190 |
+
"BasicConstraints",
|
| 191 |
+
"CRLDistributionPoints",
|
| 192 |
+
"CRLNumber",
|
| 193 |
+
"CRLReason",
|
| 194 |
+
"Certificate",
|
| 195 |
+
"CertificateBuilder",
|
| 196 |
+
"CertificateIssuer",
|
| 197 |
+
"CertificatePolicies",
|
| 198 |
+
"CertificateRevocationList",
|
| 199 |
+
"CertificateRevocationListBuilder",
|
| 200 |
+
"CertificateSigningRequest",
|
| 201 |
+
"CertificateSigningRequestBuilder",
|
| 202 |
+
"DNSName",
|
| 203 |
+
"DeltaCRLIndicator",
|
| 204 |
+
"DirectoryName",
|
| 205 |
+
"DistributionPoint",
|
| 206 |
+
"DuplicateExtension",
|
| 207 |
+
"ExtendedKeyUsage",
|
| 208 |
+
"Extension",
|
| 209 |
+
"ExtensionNotFound",
|
| 210 |
+
"ExtensionType",
|
| 211 |
+
"Extensions",
|
| 212 |
+
"FreshestCRL",
|
| 213 |
+
"GeneralName",
|
| 214 |
+
"GeneralNames",
|
| 215 |
+
"IPAddress",
|
| 216 |
+
"InhibitAnyPolicy",
|
| 217 |
+
"InvalidVersion",
|
| 218 |
+
"InvalidityDate",
|
| 219 |
+
"IssuerAlternativeName",
|
| 220 |
+
"IssuingDistributionPoint",
|
| 221 |
+
"KeyUsage",
|
| 222 |
+
"MSCertificateTemplate",
|
| 223 |
+
"Name",
|
| 224 |
+
"NameAttribute",
|
| 225 |
+
"NameConstraints",
|
| 226 |
+
"NameOID",
|
| 227 |
+
"NamingAuthority",
|
| 228 |
+
"NoticeReference",
|
| 229 |
+
"OCSPAcceptableResponses",
|
| 230 |
+
"OCSPNoCheck",
|
| 231 |
+
"OCSPNonce",
|
| 232 |
+
"ObjectIdentifier",
|
| 233 |
+
"OtherName",
|
| 234 |
+
"PolicyConstraints",
|
| 235 |
+
"PolicyInformation",
|
| 236 |
+
"PrecertPoison",
|
| 237 |
+
"PrecertificateSignedCertificateTimestamps",
|
| 238 |
+
"PrivateKeyUsagePeriod",
|
| 239 |
+
"ProfessionInfo",
|
| 240 |
+
"PublicKeyAlgorithmOID",
|
| 241 |
+
"RFC822Name",
|
| 242 |
+
"ReasonFlags",
|
| 243 |
+
"RegisteredID",
|
| 244 |
+
"RelativeDistinguishedName",
|
| 245 |
+
"RevokedCertificate",
|
| 246 |
+
"RevokedCertificateBuilder",
|
| 247 |
+
"SignatureAlgorithmOID",
|
| 248 |
+
"SignedCertificateTimestamps",
|
| 249 |
+
"SubjectAlternativeName",
|
| 250 |
+
"SubjectInformationAccess",
|
| 251 |
+
"SubjectKeyIdentifier",
|
| 252 |
+
"TLSFeature",
|
| 253 |
+
"TLSFeatureType",
|
| 254 |
+
"UniformResourceIdentifier",
|
| 255 |
+
"UnrecognizedExtension",
|
| 256 |
+
"UnsupportedGeneralNameType",
|
| 257 |
+
"UserNotice",
|
| 258 |
+
"Version",
|
| 259 |
+
"certificate_transparency",
|
| 260 |
+
"load_der_x509_certificate",
|
| 261 |
+
"load_der_x509_crl",
|
| 262 |
+
"load_der_x509_csr",
|
| 263 |
+
"load_pem_x509_certificate",
|
| 264 |
+
"load_pem_x509_certificates",
|
| 265 |
+
"load_pem_x509_crl",
|
| 266 |
+
"load_pem_x509_csr",
|
| 267 |
+
"oid",
|
| 268 |
+
"random_serial_number",
|
| 269 |
+
"verification",
|
| 270 |
+
]
|
.mcp-venv/lib/python3.13/site-packages/cryptography/x509/base.py
ADDED
|
@@ -0,0 +1,834 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import datetime
|
| 8 |
+
import os
|
| 9 |
+
import typing
|
| 10 |
+
from collections.abc import Iterable
|
| 11 |
+
|
| 12 |
+
from cryptography import utils
|
| 13 |
+
from cryptography.hazmat.bindings._rust import x509 as rust_x509
|
| 14 |
+
from cryptography.hazmat.primitives import hashes
|
| 15 |
+
from cryptography.hazmat.primitives.asymmetric import (
|
| 16 |
+
dsa,
|
| 17 |
+
ec,
|
| 18 |
+
ed448,
|
| 19 |
+
ed25519,
|
| 20 |
+
mldsa,
|
| 21 |
+
mlkem,
|
| 22 |
+
padding,
|
| 23 |
+
rsa,
|
| 24 |
+
x448,
|
| 25 |
+
x25519,
|
| 26 |
+
)
|
| 27 |
+
from cryptography.hazmat.primitives.asymmetric.types import (
|
| 28 |
+
CertificateIssuerPrivateKeyTypes,
|
| 29 |
+
CertificatePublicKeyTypes,
|
| 30 |
+
)
|
| 31 |
+
from cryptography.x509.extensions import (
|
| 32 |
+
Extension,
|
| 33 |
+
ExtensionType,
|
| 34 |
+
_make_sequence_methods,
|
| 35 |
+
)
|
| 36 |
+
from cryptography.x509.name import Name, _ASN1Type
|
| 37 |
+
from cryptography.x509.oid import ObjectIdentifier
|
| 38 |
+
|
| 39 |
+
_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1)
|
| 40 |
+
|
| 41 |
+
# This must be kept in sync with sign.rs's list of allowable types in
|
| 42 |
+
# identify_hash_type
|
| 43 |
+
_AllowedHashTypes = typing.Union[
|
| 44 |
+
hashes.SHA224,
|
| 45 |
+
hashes.SHA256,
|
| 46 |
+
hashes.SHA384,
|
| 47 |
+
hashes.SHA512,
|
| 48 |
+
hashes.SHA3_224,
|
| 49 |
+
hashes.SHA3_256,
|
| 50 |
+
hashes.SHA3_384,
|
| 51 |
+
hashes.SHA3_512,
|
| 52 |
+
]
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class AttributeNotFound(Exception):
|
| 56 |
+
def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
|
| 57 |
+
super().__init__(msg)
|
| 58 |
+
self.oid = oid
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def _reject_duplicate_extension(
|
| 62 |
+
extension: Extension[ExtensionType],
|
| 63 |
+
extensions: list[Extension[ExtensionType]],
|
| 64 |
+
) -> None:
|
| 65 |
+
# This is quadratic in the number of extensions
|
| 66 |
+
for e in extensions:
|
| 67 |
+
if e.oid == extension.oid:
|
| 68 |
+
raise ValueError("This extension has already been set.")
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def _reject_duplicate_attribute(
|
| 72 |
+
oid: ObjectIdentifier,
|
| 73 |
+
attributes: list[tuple[ObjectIdentifier, bytes, int | None]],
|
| 74 |
+
) -> None:
|
| 75 |
+
# This is quadratic in the number of attributes
|
| 76 |
+
for attr_oid, _, _ in attributes:
|
| 77 |
+
if attr_oid == oid:
|
| 78 |
+
raise ValueError("This attribute has already been set.")
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime:
|
| 82 |
+
"""Normalizes a datetime to a naive datetime in UTC.
|
| 83 |
+
|
| 84 |
+
time -- datetime to normalize. Assumed to be in UTC if not timezone
|
| 85 |
+
aware.
|
| 86 |
+
"""
|
| 87 |
+
if time.tzinfo is not None:
|
| 88 |
+
offset = time.utcoffset()
|
| 89 |
+
offset = offset if offset else datetime.timedelta()
|
| 90 |
+
return time.replace(tzinfo=None) - offset
|
| 91 |
+
else:
|
| 92 |
+
return time
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
class Attribute:
|
| 96 |
+
def __init__(
|
| 97 |
+
self,
|
| 98 |
+
oid: ObjectIdentifier,
|
| 99 |
+
value: bytes,
|
| 100 |
+
_type: int = _ASN1Type.UTF8String.value,
|
| 101 |
+
) -> None:
|
| 102 |
+
self._oid = oid
|
| 103 |
+
self._value = value
|
| 104 |
+
self._type = _type
|
| 105 |
+
|
| 106 |
+
@property
|
| 107 |
+
def oid(self) -> ObjectIdentifier:
|
| 108 |
+
return self._oid
|
| 109 |
+
|
| 110 |
+
@property
|
| 111 |
+
def value(self) -> bytes:
|
| 112 |
+
return self._value
|
| 113 |
+
|
| 114 |
+
def __repr__(self) -> str:
|
| 115 |
+
return f"<Attribute(oid={self.oid}, value={self.value!r})>"
|
| 116 |
+
|
| 117 |
+
def __eq__(self, other: object) -> bool:
|
| 118 |
+
if not isinstance(other, Attribute):
|
| 119 |
+
return NotImplemented
|
| 120 |
+
|
| 121 |
+
return (
|
| 122 |
+
self.oid == other.oid
|
| 123 |
+
and self.value == other.value
|
| 124 |
+
and self._type == other._type
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
def __hash__(self) -> int:
|
| 128 |
+
return hash((self.oid, self.value, self._type))
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
class Attributes:
|
| 132 |
+
def __init__(
|
| 133 |
+
self,
|
| 134 |
+
attributes: Iterable[Attribute],
|
| 135 |
+
) -> None:
|
| 136 |
+
self._attributes = list(attributes)
|
| 137 |
+
|
| 138 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_attributes")
|
| 139 |
+
|
| 140 |
+
def __repr__(self) -> str:
|
| 141 |
+
return f"<Attributes({self._attributes})>"
|
| 142 |
+
|
| 143 |
+
def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute:
|
| 144 |
+
for attr in self:
|
| 145 |
+
if attr.oid == oid:
|
| 146 |
+
return attr
|
| 147 |
+
|
| 148 |
+
raise AttributeNotFound(f"No {oid} attribute was found", oid)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
class Version(utils.Enum):
|
| 152 |
+
v1 = 0
|
| 153 |
+
v3 = 2
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class InvalidVersion(Exception):
|
| 157 |
+
def __init__(self, msg: str, parsed_version: int) -> None:
|
| 158 |
+
super().__init__(msg)
|
| 159 |
+
self.parsed_version = parsed_version
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
Certificate = rust_x509.Certificate
|
| 163 |
+
RevokedCertificate = rust_x509.RevokedCertificate
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
CertificateRevocationList = rust_x509.CertificateRevocationList
|
| 167 |
+
CertificateSigningRequest = rust_x509.CertificateSigningRequest
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
load_pem_x509_certificate = rust_x509.load_pem_x509_certificate
|
| 171 |
+
load_der_x509_certificate = rust_x509.load_der_x509_certificate
|
| 172 |
+
|
| 173 |
+
load_pem_x509_certificates = rust_x509.load_pem_x509_certificates
|
| 174 |
+
|
| 175 |
+
load_pem_x509_csr = rust_x509.load_pem_x509_csr
|
| 176 |
+
load_der_x509_csr = rust_x509.load_der_x509_csr
|
| 177 |
+
|
| 178 |
+
load_pem_x509_crl = rust_x509.load_pem_x509_crl
|
| 179 |
+
load_der_x509_crl = rust_x509.load_der_x509_crl
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
class CertificateSigningRequestBuilder:
|
| 183 |
+
def __init__(
|
| 184 |
+
self,
|
| 185 |
+
subject_name: Name | None = None,
|
| 186 |
+
extensions: list[Extension[ExtensionType]] = [],
|
| 187 |
+
attributes: list[tuple[ObjectIdentifier, bytes, int | None]] = [],
|
| 188 |
+
):
|
| 189 |
+
"""
|
| 190 |
+
Creates an empty X.509 certificate request (v1).
|
| 191 |
+
"""
|
| 192 |
+
self._subject_name = subject_name
|
| 193 |
+
self._extensions = extensions
|
| 194 |
+
self._attributes = attributes
|
| 195 |
+
|
| 196 |
+
def subject_name(self, name: Name) -> CertificateSigningRequestBuilder:
|
| 197 |
+
"""
|
| 198 |
+
Sets the certificate requestor's distinguished name.
|
| 199 |
+
"""
|
| 200 |
+
if not isinstance(name, Name):
|
| 201 |
+
raise TypeError("Expecting x509.Name object.")
|
| 202 |
+
if self._subject_name is not None:
|
| 203 |
+
raise ValueError("The subject name may only be set once.")
|
| 204 |
+
return CertificateSigningRequestBuilder(
|
| 205 |
+
name, self._extensions, self._attributes
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
def add_extension(
|
| 209 |
+
self, extval: ExtensionType, critical: bool
|
| 210 |
+
) -> CertificateSigningRequestBuilder:
|
| 211 |
+
"""
|
| 212 |
+
Adds an X.509 extension to the certificate request.
|
| 213 |
+
"""
|
| 214 |
+
if not isinstance(extval, ExtensionType):
|
| 215 |
+
raise TypeError("extension must be an ExtensionType")
|
| 216 |
+
|
| 217 |
+
extension = Extension(extval.oid, critical, extval)
|
| 218 |
+
_reject_duplicate_extension(extension, self._extensions)
|
| 219 |
+
|
| 220 |
+
return CertificateSigningRequestBuilder(
|
| 221 |
+
self._subject_name,
|
| 222 |
+
[*self._extensions, extension],
|
| 223 |
+
self._attributes,
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
def add_attribute(
|
| 227 |
+
self,
|
| 228 |
+
oid: ObjectIdentifier,
|
| 229 |
+
value: bytes,
|
| 230 |
+
*,
|
| 231 |
+
_tag: _ASN1Type | None = None,
|
| 232 |
+
) -> CertificateSigningRequestBuilder:
|
| 233 |
+
"""
|
| 234 |
+
Adds an X.509 attribute with an OID and associated value.
|
| 235 |
+
"""
|
| 236 |
+
if not isinstance(oid, ObjectIdentifier):
|
| 237 |
+
raise TypeError("oid must be an ObjectIdentifier")
|
| 238 |
+
|
| 239 |
+
if not isinstance(value, bytes):
|
| 240 |
+
raise TypeError("value must be bytes")
|
| 241 |
+
|
| 242 |
+
if _tag is not None and not isinstance(_tag, _ASN1Type):
|
| 243 |
+
raise TypeError("tag must be _ASN1Type")
|
| 244 |
+
|
| 245 |
+
_reject_duplicate_attribute(oid, self._attributes)
|
| 246 |
+
|
| 247 |
+
if _tag is not None:
|
| 248 |
+
tag = _tag.value
|
| 249 |
+
else:
|
| 250 |
+
tag = None
|
| 251 |
+
|
| 252 |
+
return CertificateSigningRequestBuilder(
|
| 253 |
+
self._subject_name,
|
| 254 |
+
self._extensions,
|
| 255 |
+
[*self._attributes, (oid, value, tag)],
|
| 256 |
+
)
|
| 257 |
+
|
| 258 |
+
def sign(
|
| 259 |
+
self,
|
| 260 |
+
private_key: CertificateIssuerPrivateKeyTypes,
|
| 261 |
+
algorithm: _AllowedHashTypes | None,
|
| 262 |
+
backend: typing.Any = None,
|
| 263 |
+
*,
|
| 264 |
+
rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
|
| 265 |
+
ecdsa_deterministic: bool | None = None,
|
| 266 |
+
) -> CertificateSigningRequest:
|
| 267 |
+
"""
|
| 268 |
+
Signs the request using the requestor's private key.
|
| 269 |
+
"""
|
| 270 |
+
if self._subject_name is None:
|
| 271 |
+
raise ValueError("A CertificateSigningRequest must have a subject")
|
| 272 |
+
|
| 273 |
+
if rsa_padding is not None:
|
| 274 |
+
if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
|
| 275 |
+
raise TypeError("Padding must be PSS or PKCS1v15")
|
| 276 |
+
if not isinstance(private_key, rsa.RSAPrivateKey):
|
| 277 |
+
raise TypeError("Padding is only supported for RSA keys")
|
| 278 |
+
|
| 279 |
+
if ecdsa_deterministic is not None:
|
| 280 |
+
if not isinstance(private_key, ec.EllipticCurvePrivateKey):
|
| 281 |
+
raise TypeError(
|
| 282 |
+
"Deterministic ECDSA is only supported for EC keys"
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
return rust_x509.create_x509_csr(
|
| 286 |
+
self,
|
| 287 |
+
private_key,
|
| 288 |
+
algorithm,
|
| 289 |
+
rsa_padding,
|
| 290 |
+
ecdsa_deterministic,
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
class CertificateBuilder:
|
| 295 |
+
_extensions: list[Extension[ExtensionType]]
|
| 296 |
+
|
| 297 |
+
def __init__(
|
| 298 |
+
self,
|
| 299 |
+
issuer_name: Name | None = None,
|
| 300 |
+
subject_name: Name | None = None,
|
| 301 |
+
public_key: CertificatePublicKeyTypes | None = None,
|
| 302 |
+
serial_number: int | None = None,
|
| 303 |
+
not_valid_before: datetime.datetime | None = None,
|
| 304 |
+
not_valid_after: datetime.datetime | None = None,
|
| 305 |
+
extensions: list[Extension[ExtensionType]] = [],
|
| 306 |
+
public_key_rsa_padding: type[padding.PSS] | None = None,
|
| 307 |
+
) -> None:
|
| 308 |
+
self._version = Version.v3
|
| 309 |
+
self._issuer_name = issuer_name
|
| 310 |
+
self._subject_name = subject_name
|
| 311 |
+
self._public_key = public_key
|
| 312 |
+
self._serial_number = serial_number
|
| 313 |
+
self._not_valid_before = not_valid_before
|
| 314 |
+
self._not_valid_after = not_valid_after
|
| 315 |
+
self._extensions = extensions
|
| 316 |
+
self._public_key_rsa_padding = public_key_rsa_padding
|
| 317 |
+
|
| 318 |
+
def issuer_name(self, name: Name) -> CertificateBuilder:
|
| 319 |
+
"""
|
| 320 |
+
Sets the CA's distinguished name.
|
| 321 |
+
"""
|
| 322 |
+
if not isinstance(name, Name):
|
| 323 |
+
raise TypeError("Expecting x509.Name object.")
|
| 324 |
+
if self._issuer_name is not None:
|
| 325 |
+
raise ValueError("The issuer name may only be set once.")
|
| 326 |
+
return CertificateBuilder(
|
| 327 |
+
name,
|
| 328 |
+
self._subject_name,
|
| 329 |
+
self._public_key,
|
| 330 |
+
self._serial_number,
|
| 331 |
+
self._not_valid_before,
|
| 332 |
+
self._not_valid_after,
|
| 333 |
+
self._extensions,
|
| 334 |
+
self._public_key_rsa_padding,
|
| 335 |
+
)
|
| 336 |
+
|
| 337 |
+
def subject_name(self, name: Name) -> CertificateBuilder:
|
| 338 |
+
"""
|
| 339 |
+
Sets the requestor's distinguished name.
|
| 340 |
+
"""
|
| 341 |
+
if not isinstance(name, Name):
|
| 342 |
+
raise TypeError("Expecting x509.Name object.")
|
| 343 |
+
if self._subject_name is not None:
|
| 344 |
+
raise ValueError("The subject name may only be set once.")
|
| 345 |
+
return CertificateBuilder(
|
| 346 |
+
self._issuer_name,
|
| 347 |
+
name,
|
| 348 |
+
self._public_key,
|
| 349 |
+
self._serial_number,
|
| 350 |
+
self._not_valid_before,
|
| 351 |
+
self._not_valid_after,
|
| 352 |
+
self._extensions,
|
| 353 |
+
self._public_key_rsa_padding,
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
def public_key(
|
| 357 |
+
self,
|
| 358 |
+
key: CertificatePublicKeyTypes,
|
| 359 |
+
*,
|
| 360 |
+
rsa_padding: type[padding.PSS] | None = None,
|
| 361 |
+
) -> CertificateBuilder:
|
| 362 |
+
"""
|
| 363 |
+
Sets the requestor's public key (as found in the signing request).
|
| 364 |
+
"""
|
| 365 |
+
if not isinstance(
|
| 366 |
+
key,
|
| 367 |
+
(
|
| 368 |
+
dsa.DSAPublicKey,
|
| 369 |
+
rsa.RSAPublicKey,
|
| 370 |
+
ec.EllipticCurvePublicKey,
|
| 371 |
+
ed25519.Ed25519PublicKey,
|
| 372 |
+
ed448.Ed448PublicKey,
|
| 373 |
+
mldsa.MLDSA44PublicKey,
|
| 374 |
+
mldsa.MLDSA65PublicKey,
|
| 375 |
+
mldsa.MLDSA87PublicKey,
|
| 376 |
+
mlkem.MLKEM768PublicKey,
|
| 377 |
+
mlkem.MLKEM1024PublicKey,
|
| 378 |
+
x25519.X25519PublicKey,
|
| 379 |
+
x448.X448PublicKey,
|
| 380 |
+
),
|
| 381 |
+
):
|
| 382 |
+
raise TypeError(
|
| 383 |
+
"Expecting one of DSAPublicKey, RSAPublicKey,"
|
| 384 |
+
" EllipticCurvePublicKey, Ed25519PublicKey,"
|
| 385 |
+
" Ed448PublicKey, MLDSA44PublicKey, MLDSA65PublicKey,"
|
| 386 |
+
" MLDSA87PublicKey, MLKEM768PublicKey, MLKEM1024PublicKey,"
|
| 387 |
+
" X25519PublicKey or X448PublicKey."
|
| 388 |
+
)
|
| 389 |
+
if rsa_padding is not None:
|
| 390 |
+
if rsa_padding is not padding.PSS:
|
| 391 |
+
raise TypeError(
|
| 392 |
+
"rsa_padding must be the PSS class, not an instance"
|
| 393 |
+
)
|
| 394 |
+
if not isinstance(key, rsa.RSAPublicKey):
|
| 395 |
+
raise TypeError(
|
| 396 |
+
"rsa_padding is only supported with RSA public keys"
|
| 397 |
+
)
|
| 398 |
+
if self._public_key is not None:
|
| 399 |
+
raise ValueError("The public key may only be set once.")
|
| 400 |
+
return CertificateBuilder(
|
| 401 |
+
self._issuer_name,
|
| 402 |
+
self._subject_name,
|
| 403 |
+
key,
|
| 404 |
+
self._serial_number,
|
| 405 |
+
self._not_valid_before,
|
| 406 |
+
self._not_valid_after,
|
| 407 |
+
self._extensions,
|
| 408 |
+
rsa_padding,
|
| 409 |
+
)
|
| 410 |
+
|
| 411 |
+
def serial_number(self, number: int) -> CertificateBuilder:
|
| 412 |
+
"""
|
| 413 |
+
Sets the certificate serial number.
|
| 414 |
+
"""
|
| 415 |
+
if not isinstance(number, int):
|
| 416 |
+
raise TypeError("Serial number must be of integral type.")
|
| 417 |
+
if self._serial_number is not None:
|
| 418 |
+
raise ValueError("The serial number may only be set once.")
|
| 419 |
+
if number <= 0:
|
| 420 |
+
raise ValueError("The serial number should be positive.")
|
| 421 |
+
|
| 422 |
+
# ASN.1 integers are always signed, so most significant bit must be
|
| 423 |
+
# zero.
|
| 424 |
+
if number.bit_length() >= 160: # As defined in RFC 5280
|
| 425 |
+
raise ValueError(
|
| 426 |
+
"The serial number should not be more than 159 bits."
|
| 427 |
+
)
|
| 428 |
+
return CertificateBuilder(
|
| 429 |
+
self._issuer_name,
|
| 430 |
+
self._subject_name,
|
| 431 |
+
self._public_key,
|
| 432 |
+
number,
|
| 433 |
+
self._not_valid_before,
|
| 434 |
+
self._not_valid_after,
|
| 435 |
+
self._extensions,
|
| 436 |
+
self._public_key_rsa_padding,
|
| 437 |
+
)
|
| 438 |
+
|
| 439 |
+
def not_valid_before(self, time: datetime.datetime) -> CertificateBuilder:
|
| 440 |
+
"""
|
| 441 |
+
Sets the certificate activation time.
|
| 442 |
+
"""
|
| 443 |
+
if not isinstance(time, datetime.datetime):
|
| 444 |
+
raise TypeError("Expecting datetime object.")
|
| 445 |
+
if self._not_valid_before is not None:
|
| 446 |
+
raise ValueError("The not valid before may only be set once.")
|
| 447 |
+
time = _convert_to_naive_utc_time(time)
|
| 448 |
+
if time < _EARLIEST_UTC_TIME:
|
| 449 |
+
raise ValueError(
|
| 450 |
+
"The not valid before date must be on or after"
|
| 451 |
+
" 1950 January 1)."
|
| 452 |
+
)
|
| 453 |
+
if self._not_valid_after is not None and time > self._not_valid_after:
|
| 454 |
+
raise ValueError(
|
| 455 |
+
"The not valid before date must be before the not valid after "
|
| 456 |
+
"date."
|
| 457 |
+
)
|
| 458 |
+
return CertificateBuilder(
|
| 459 |
+
self._issuer_name,
|
| 460 |
+
self._subject_name,
|
| 461 |
+
self._public_key,
|
| 462 |
+
self._serial_number,
|
| 463 |
+
time,
|
| 464 |
+
self._not_valid_after,
|
| 465 |
+
self._extensions,
|
| 466 |
+
self._public_key_rsa_padding,
|
| 467 |
+
)
|
| 468 |
+
|
| 469 |
+
def not_valid_after(self, time: datetime.datetime) -> CertificateBuilder:
|
| 470 |
+
"""
|
| 471 |
+
Sets the certificate expiration time.
|
| 472 |
+
"""
|
| 473 |
+
if not isinstance(time, datetime.datetime):
|
| 474 |
+
raise TypeError("Expecting datetime object.")
|
| 475 |
+
if self._not_valid_after is not None:
|
| 476 |
+
raise ValueError("The not valid after may only be set once.")
|
| 477 |
+
time = _convert_to_naive_utc_time(time)
|
| 478 |
+
if time < _EARLIEST_UTC_TIME:
|
| 479 |
+
raise ValueError(
|
| 480 |
+
"The not valid after date must be on or after 1950 January 1."
|
| 481 |
+
)
|
| 482 |
+
if (
|
| 483 |
+
self._not_valid_before is not None
|
| 484 |
+
and time < self._not_valid_before
|
| 485 |
+
):
|
| 486 |
+
raise ValueError(
|
| 487 |
+
"The not valid after date must be after the not valid before "
|
| 488 |
+
"date."
|
| 489 |
+
)
|
| 490 |
+
return CertificateBuilder(
|
| 491 |
+
self._issuer_name,
|
| 492 |
+
self._subject_name,
|
| 493 |
+
self._public_key,
|
| 494 |
+
self._serial_number,
|
| 495 |
+
self._not_valid_before,
|
| 496 |
+
time,
|
| 497 |
+
self._extensions,
|
| 498 |
+
self._public_key_rsa_padding,
|
| 499 |
+
)
|
| 500 |
+
|
| 501 |
+
def add_extension(
|
| 502 |
+
self, extval: ExtensionType, critical: bool
|
| 503 |
+
) -> CertificateBuilder:
|
| 504 |
+
"""
|
| 505 |
+
Adds an X.509 extension to the certificate.
|
| 506 |
+
"""
|
| 507 |
+
if not isinstance(extval, ExtensionType):
|
| 508 |
+
raise TypeError("extension must be an ExtensionType")
|
| 509 |
+
|
| 510 |
+
extension = Extension(extval.oid, critical, extval)
|
| 511 |
+
_reject_duplicate_extension(extension, self._extensions)
|
| 512 |
+
|
| 513 |
+
return CertificateBuilder(
|
| 514 |
+
self._issuer_name,
|
| 515 |
+
self._subject_name,
|
| 516 |
+
self._public_key,
|
| 517 |
+
self._serial_number,
|
| 518 |
+
self._not_valid_before,
|
| 519 |
+
self._not_valid_after,
|
| 520 |
+
[*self._extensions, extension],
|
| 521 |
+
self._public_key_rsa_padding,
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
def sign(
|
| 525 |
+
self,
|
| 526 |
+
private_key: CertificateIssuerPrivateKeyTypes,
|
| 527 |
+
algorithm: _AllowedHashTypes | None,
|
| 528 |
+
backend: typing.Any = None,
|
| 529 |
+
*,
|
| 530 |
+
rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
|
| 531 |
+
ecdsa_deterministic: bool | None = None,
|
| 532 |
+
) -> Certificate:
|
| 533 |
+
"""
|
| 534 |
+
Signs the certificate using the CA's private key.
|
| 535 |
+
"""
|
| 536 |
+
if self._subject_name is None:
|
| 537 |
+
raise ValueError("A certificate must have a subject name")
|
| 538 |
+
|
| 539 |
+
if self._issuer_name is None:
|
| 540 |
+
raise ValueError("A certificate must have an issuer name")
|
| 541 |
+
|
| 542 |
+
if self._serial_number is None:
|
| 543 |
+
raise ValueError("A certificate must have a serial number")
|
| 544 |
+
|
| 545 |
+
if self._not_valid_before is None:
|
| 546 |
+
raise ValueError("A certificate must have a not valid before time")
|
| 547 |
+
|
| 548 |
+
if self._not_valid_after is None:
|
| 549 |
+
raise ValueError("A certificate must have a not valid after time")
|
| 550 |
+
|
| 551 |
+
if self._public_key is None:
|
| 552 |
+
raise ValueError("A certificate must have a public key")
|
| 553 |
+
|
| 554 |
+
if private_key is None:
|
| 555 |
+
raise TypeError("Need private key to sign certificate")
|
| 556 |
+
|
| 557 |
+
if rsa_padding is not None:
|
| 558 |
+
if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
|
| 559 |
+
raise TypeError("Padding must be PSS or PKCS1v15")
|
| 560 |
+
if not isinstance(private_key, rsa.RSAPrivateKey):
|
| 561 |
+
raise TypeError("Padding is only supported for RSA keys")
|
| 562 |
+
|
| 563 |
+
if ecdsa_deterministic is not None:
|
| 564 |
+
if not isinstance(private_key, ec.EllipticCurvePrivateKey):
|
| 565 |
+
raise TypeError(
|
| 566 |
+
"Deterministic ECDSA is only supported for EC keys"
|
| 567 |
+
)
|
| 568 |
+
|
| 569 |
+
return rust_x509.create_x509_certificate(
|
| 570 |
+
self,
|
| 571 |
+
private_key,
|
| 572 |
+
algorithm,
|
| 573 |
+
rsa_padding,
|
| 574 |
+
ecdsa_deterministic,
|
| 575 |
+
)
|
| 576 |
+
|
| 577 |
+
def create_unsigned(self) -> Certificate:
|
| 578 |
+
"""
|
| 579 |
+
Creates an unsigned certificate, per RFC 9925.
|
| 580 |
+
"""
|
| 581 |
+
if self._subject_name is None:
|
| 582 |
+
raise ValueError("A certificate must have a subject name")
|
| 583 |
+
|
| 584 |
+
if self._issuer_name is None:
|
| 585 |
+
raise ValueError("A certificate must have an issuer name")
|
| 586 |
+
|
| 587 |
+
if self._serial_number is None:
|
| 588 |
+
raise ValueError("A certificate must have a serial number")
|
| 589 |
+
|
| 590 |
+
if self._not_valid_before is None:
|
| 591 |
+
raise ValueError("A certificate must have a not valid before time")
|
| 592 |
+
|
| 593 |
+
if self._not_valid_after is None:
|
| 594 |
+
raise ValueError("A certificate must have a not valid after time")
|
| 595 |
+
|
| 596 |
+
if self._public_key is None:
|
| 597 |
+
raise ValueError("A certificate must have a public key")
|
| 598 |
+
|
| 599 |
+
return rust_x509.create_x509_certificate(
|
| 600 |
+
self,
|
| 601 |
+
private_key=None,
|
| 602 |
+
hash_algorithm=None,
|
| 603 |
+
rsa_padding=None,
|
| 604 |
+
ecdsa_deterministic=None,
|
| 605 |
+
)
|
| 606 |
+
|
| 607 |
+
|
| 608 |
+
class CertificateRevocationListBuilder:
|
| 609 |
+
_extensions: list[Extension[ExtensionType]]
|
| 610 |
+
_revoked_certificates: list[RevokedCertificate]
|
| 611 |
+
|
| 612 |
+
def __init__(
|
| 613 |
+
self,
|
| 614 |
+
issuer_name: Name | None = None,
|
| 615 |
+
last_update: datetime.datetime | None = None,
|
| 616 |
+
next_update: datetime.datetime | None = None,
|
| 617 |
+
extensions: list[Extension[ExtensionType]] = [],
|
| 618 |
+
revoked_certificates: list[RevokedCertificate] = [],
|
| 619 |
+
):
|
| 620 |
+
self._issuer_name = issuer_name
|
| 621 |
+
self._last_update = last_update
|
| 622 |
+
self._next_update = next_update
|
| 623 |
+
self._extensions = extensions
|
| 624 |
+
self._revoked_certificates = revoked_certificates
|
| 625 |
+
|
| 626 |
+
def issuer_name(
|
| 627 |
+
self, issuer_name: Name
|
| 628 |
+
) -> CertificateRevocationListBuilder:
|
| 629 |
+
if not isinstance(issuer_name, Name):
|
| 630 |
+
raise TypeError("Expecting x509.Name object.")
|
| 631 |
+
if self._issuer_name is not None:
|
| 632 |
+
raise ValueError("The issuer name may only be set once.")
|
| 633 |
+
return CertificateRevocationListBuilder(
|
| 634 |
+
issuer_name,
|
| 635 |
+
self._last_update,
|
| 636 |
+
self._next_update,
|
| 637 |
+
self._extensions,
|
| 638 |
+
self._revoked_certificates,
|
| 639 |
+
)
|
| 640 |
+
|
| 641 |
+
def last_update(
|
| 642 |
+
self, last_update: datetime.datetime
|
| 643 |
+
) -> CertificateRevocationListBuilder:
|
| 644 |
+
if not isinstance(last_update, datetime.datetime):
|
| 645 |
+
raise TypeError("Expecting datetime object.")
|
| 646 |
+
if self._last_update is not None:
|
| 647 |
+
raise ValueError("Last update may only be set once.")
|
| 648 |
+
last_update = _convert_to_naive_utc_time(last_update)
|
| 649 |
+
if last_update < _EARLIEST_UTC_TIME:
|
| 650 |
+
raise ValueError(
|
| 651 |
+
"The last update date must be on or after 1950 January 1."
|
| 652 |
+
)
|
| 653 |
+
if self._next_update is not None and last_update > self._next_update:
|
| 654 |
+
raise ValueError(
|
| 655 |
+
"The last update date must be before the next update date."
|
| 656 |
+
)
|
| 657 |
+
return CertificateRevocationListBuilder(
|
| 658 |
+
self._issuer_name,
|
| 659 |
+
last_update,
|
| 660 |
+
self._next_update,
|
| 661 |
+
self._extensions,
|
| 662 |
+
self._revoked_certificates,
|
| 663 |
+
)
|
| 664 |
+
|
| 665 |
+
def next_update(
|
| 666 |
+
self, next_update: datetime.datetime
|
| 667 |
+
) -> CertificateRevocationListBuilder:
|
| 668 |
+
if not isinstance(next_update, datetime.datetime):
|
| 669 |
+
raise TypeError("Expecting datetime object.")
|
| 670 |
+
if self._next_update is not None:
|
| 671 |
+
raise ValueError("Last update may only be set once.")
|
| 672 |
+
next_update = _convert_to_naive_utc_time(next_update)
|
| 673 |
+
if next_update < _EARLIEST_UTC_TIME:
|
| 674 |
+
raise ValueError(
|
| 675 |
+
"The last update date must be on or after 1950 January 1."
|
| 676 |
+
)
|
| 677 |
+
if self._last_update is not None and next_update < self._last_update:
|
| 678 |
+
raise ValueError(
|
| 679 |
+
"The next update date must be after the last update date."
|
| 680 |
+
)
|
| 681 |
+
return CertificateRevocationListBuilder(
|
| 682 |
+
self._issuer_name,
|
| 683 |
+
self._last_update,
|
| 684 |
+
next_update,
|
| 685 |
+
self._extensions,
|
| 686 |
+
self._revoked_certificates,
|
| 687 |
+
)
|
| 688 |
+
|
| 689 |
+
def add_extension(
|
| 690 |
+
self, extval: ExtensionType, critical: bool
|
| 691 |
+
) -> CertificateRevocationListBuilder:
|
| 692 |
+
"""
|
| 693 |
+
Adds an X.509 extension to the certificate revocation list.
|
| 694 |
+
"""
|
| 695 |
+
if not isinstance(extval, ExtensionType):
|
| 696 |
+
raise TypeError("extension must be an ExtensionType")
|
| 697 |
+
|
| 698 |
+
extension = Extension(extval.oid, critical, extval)
|
| 699 |
+
_reject_duplicate_extension(extension, self._extensions)
|
| 700 |
+
return CertificateRevocationListBuilder(
|
| 701 |
+
self._issuer_name,
|
| 702 |
+
self._last_update,
|
| 703 |
+
self._next_update,
|
| 704 |
+
[*self._extensions, extension],
|
| 705 |
+
self._revoked_certificates,
|
| 706 |
+
)
|
| 707 |
+
|
| 708 |
+
def add_revoked_certificate(
|
| 709 |
+
self, revoked_certificate: RevokedCertificate
|
| 710 |
+
) -> CertificateRevocationListBuilder:
|
| 711 |
+
"""
|
| 712 |
+
Adds a revoked certificate to the CRL.
|
| 713 |
+
"""
|
| 714 |
+
if not isinstance(revoked_certificate, RevokedCertificate):
|
| 715 |
+
raise TypeError("Must be an instance of RevokedCertificate")
|
| 716 |
+
|
| 717 |
+
return CertificateRevocationListBuilder(
|
| 718 |
+
self._issuer_name,
|
| 719 |
+
self._last_update,
|
| 720 |
+
self._next_update,
|
| 721 |
+
self._extensions,
|
| 722 |
+
[*self._revoked_certificates, revoked_certificate],
|
| 723 |
+
)
|
| 724 |
+
|
| 725 |
+
def sign(
|
| 726 |
+
self,
|
| 727 |
+
private_key: CertificateIssuerPrivateKeyTypes,
|
| 728 |
+
algorithm: _AllowedHashTypes | None,
|
| 729 |
+
backend: typing.Any = None,
|
| 730 |
+
*,
|
| 731 |
+
rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
|
| 732 |
+
ecdsa_deterministic: bool | None = None,
|
| 733 |
+
) -> CertificateRevocationList:
|
| 734 |
+
if self._issuer_name is None:
|
| 735 |
+
raise ValueError("A CRL must have an issuer name")
|
| 736 |
+
|
| 737 |
+
if self._last_update is None:
|
| 738 |
+
raise ValueError("A CRL must have a last update time")
|
| 739 |
+
|
| 740 |
+
if self._next_update is None:
|
| 741 |
+
raise ValueError("A CRL must have a next update time")
|
| 742 |
+
|
| 743 |
+
if rsa_padding is not None:
|
| 744 |
+
if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
|
| 745 |
+
raise TypeError("Padding must be PSS or PKCS1v15")
|
| 746 |
+
if not isinstance(private_key, rsa.RSAPrivateKey):
|
| 747 |
+
raise TypeError("Padding is only supported for RSA keys")
|
| 748 |
+
|
| 749 |
+
if ecdsa_deterministic is not None:
|
| 750 |
+
if not isinstance(private_key, ec.EllipticCurvePrivateKey):
|
| 751 |
+
raise TypeError(
|
| 752 |
+
"Deterministic ECDSA is only supported for EC keys"
|
| 753 |
+
)
|
| 754 |
+
|
| 755 |
+
return rust_x509.create_x509_crl(
|
| 756 |
+
self,
|
| 757 |
+
private_key,
|
| 758 |
+
algorithm,
|
| 759 |
+
rsa_padding,
|
| 760 |
+
ecdsa_deterministic,
|
| 761 |
+
)
|
| 762 |
+
|
| 763 |
+
|
| 764 |
+
class RevokedCertificateBuilder:
|
| 765 |
+
def __init__(
|
| 766 |
+
self,
|
| 767 |
+
serial_number: int | None = None,
|
| 768 |
+
revocation_date: datetime.datetime | None = None,
|
| 769 |
+
extensions: list[Extension[ExtensionType]] = [],
|
| 770 |
+
):
|
| 771 |
+
self._serial_number = serial_number
|
| 772 |
+
self._revocation_date = revocation_date
|
| 773 |
+
self._extensions = extensions
|
| 774 |
+
|
| 775 |
+
def serial_number(self, number: int) -> RevokedCertificateBuilder:
|
| 776 |
+
if not isinstance(number, int):
|
| 777 |
+
raise TypeError("Serial number must be of integral type.")
|
| 778 |
+
if self._serial_number is not None:
|
| 779 |
+
raise ValueError("The serial number may only be set once.")
|
| 780 |
+
if number <= 0:
|
| 781 |
+
raise ValueError("The serial number should be positive")
|
| 782 |
+
|
| 783 |
+
# ASN.1 integers are always signed, so most significant bit must be
|
| 784 |
+
# zero.
|
| 785 |
+
if number.bit_length() >= 160: # As defined in RFC 5280
|
| 786 |
+
raise ValueError(
|
| 787 |
+
"The serial number should not be more than 159 bits."
|
| 788 |
+
)
|
| 789 |
+
return RevokedCertificateBuilder(
|
| 790 |
+
number, self._revocation_date, self._extensions
|
| 791 |
+
)
|
| 792 |
+
|
| 793 |
+
def revocation_date(
|
| 794 |
+
self, time: datetime.datetime
|
| 795 |
+
) -> RevokedCertificateBuilder:
|
| 796 |
+
if not isinstance(time, datetime.datetime):
|
| 797 |
+
raise TypeError("Expecting datetime object.")
|
| 798 |
+
if self._revocation_date is not None:
|
| 799 |
+
raise ValueError("The revocation date may only be set once.")
|
| 800 |
+
time = _convert_to_naive_utc_time(time)
|
| 801 |
+
if time < _EARLIEST_UTC_TIME:
|
| 802 |
+
raise ValueError(
|
| 803 |
+
"The revocation date must be on or after 1950 January 1."
|
| 804 |
+
)
|
| 805 |
+
return RevokedCertificateBuilder(
|
| 806 |
+
self._serial_number, time, self._extensions
|
| 807 |
+
)
|
| 808 |
+
|
| 809 |
+
def add_extension(
|
| 810 |
+
self, extval: ExtensionType, critical: bool
|
| 811 |
+
) -> RevokedCertificateBuilder:
|
| 812 |
+
if not isinstance(extval, ExtensionType):
|
| 813 |
+
raise TypeError("extension must be an ExtensionType")
|
| 814 |
+
|
| 815 |
+
extension = Extension(extval.oid, critical, extval)
|
| 816 |
+
_reject_duplicate_extension(extension, self._extensions)
|
| 817 |
+
return RevokedCertificateBuilder(
|
| 818 |
+
self._serial_number,
|
| 819 |
+
self._revocation_date,
|
| 820 |
+
[*self._extensions, extension],
|
| 821 |
+
)
|
| 822 |
+
|
| 823 |
+
def build(self, backend: typing.Any = None) -> RevokedCertificate:
|
| 824 |
+
if self._serial_number is None:
|
| 825 |
+
raise ValueError("A revoked certificate must have a serial number")
|
| 826 |
+
if self._revocation_date is None:
|
| 827 |
+
raise ValueError(
|
| 828 |
+
"A revoked certificate must have a revocation date"
|
| 829 |
+
)
|
| 830 |
+
return rust_x509.create_revoked_certificate(self)
|
| 831 |
+
|
| 832 |
+
|
| 833 |
+
def random_serial_number() -> int:
|
| 834 |
+
return int.from_bytes(os.urandom(20), "big") >> 1
|
.mcp-venv/lib/python3.13/site-packages/cryptography/x509/certificate_transparency.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography import utils
|
| 8 |
+
from cryptography.hazmat.bindings._rust import x509 as rust_x509
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class LogEntryType(utils.Enum):
|
| 12 |
+
X509_CERTIFICATE = 0
|
| 13 |
+
PRE_CERTIFICATE = 1
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class Version(utils.Enum):
|
| 17 |
+
v1 = 0
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class SignatureAlgorithm(utils.Enum):
|
| 21 |
+
"""
|
| 22 |
+
Signature algorithms that are valid for SCTs.
|
| 23 |
+
|
| 24 |
+
These are exactly the same as SignatureAlgorithm in RFC 5246 (TLS 1.2).
|
| 25 |
+
|
| 26 |
+
See: <https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.1.4.1>
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
ANONYMOUS = 0
|
| 30 |
+
RSA = 1
|
| 31 |
+
DSA = 2
|
| 32 |
+
ECDSA = 3
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
SignedCertificateTimestamp = rust_x509.Sct
|
.mcp-venv/lib/python3.13/site-packages/cryptography/x509/extensions.py
ADDED
|
@@ -0,0 +1,2533 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import abc
|
| 8 |
+
import datetime
|
| 9 |
+
import hashlib
|
| 10 |
+
import ipaddress
|
| 11 |
+
import typing
|
| 12 |
+
from collections.abc import Iterable, Iterator
|
| 13 |
+
|
| 14 |
+
from cryptography import utils
|
| 15 |
+
from cryptography.hazmat.bindings._rust import asn1
|
| 16 |
+
from cryptography.hazmat.bindings._rust import x509 as rust_x509
|
| 17 |
+
from cryptography.hazmat.primitives import _serialization, constant_time
|
| 18 |
+
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey
|
| 19 |
+
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
|
| 20 |
+
from cryptography.hazmat.primitives.asymmetric.types import (
|
| 21 |
+
CertificateIssuerPublicKeyTypes,
|
| 22 |
+
CertificatePublicKeyTypes,
|
| 23 |
+
)
|
| 24 |
+
from cryptography.x509.certificate_transparency import (
|
| 25 |
+
SignedCertificateTimestamp,
|
| 26 |
+
)
|
| 27 |
+
from cryptography.x509.general_name import (
|
| 28 |
+
DirectoryName,
|
| 29 |
+
DNSName,
|
| 30 |
+
GeneralName,
|
| 31 |
+
IPAddress,
|
| 32 |
+
OtherName,
|
| 33 |
+
RegisteredID,
|
| 34 |
+
RFC822Name,
|
| 35 |
+
UniformResourceIdentifier,
|
| 36 |
+
_IPAddressTypes,
|
| 37 |
+
)
|
| 38 |
+
from cryptography.x509.name import Name, RelativeDistinguishedName
|
| 39 |
+
from cryptography.x509.oid import (
|
| 40 |
+
CRLEntryExtensionOID,
|
| 41 |
+
ExtensionOID,
|
| 42 |
+
ObjectIdentifier,
|
| 43 |
+
OCSPExtensionOID,
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
ExtensionTypeVar = typing.TypeVar(
|
| 47 |
+
"ExtensionTypeVar", bound="ExtensionType", covariant=True
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _key_identifier_from_public_key(
|
| 52 |
+
public_key: CertificatePublicKeyTypes,
|
| 53 |
+
) -> bytes:
|
| 54 |
+
if isinstance(public_key, RSAPublicKey):
|
| 55 |
+
data = public_key.public_bytes(
|
| 56 |
+
_serialization.Encoding.DER,
|
| 57 |
+
_serialization.PublicFormat.PKCS1,
|
| 58 |
+
)
|
| 59 |
+
elif isinstance(public_key, EllipticCurvePublicKey):
|
| 60 |
+
data = public_key.public_bytes(
|
| 61 |
+
_serialization.Encoding.X962,
|
| 62 |
+
_serialization.PublicFormat.UncompressedPoint,
|
| 63 |
+
)
|
| 64 |
+
else:
|
| 65 |
+
# This is a very slow way to do this.
|
| 66 |
+
serialized = public_key.public_bytes(
|
| 67 |
+
_serialization.Encoding.DER,
|
| 68 |
+
_serialization.PublicFormat.SubjectPublicKeyInfo,
|
| 69 |
+
)
|
| 70 |
+
data = asn1.parse_spki_for_data(serialized)
|
| 71 |
+
|
| 72 |
+
return hashlib.sha1(data).digest()
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def _make_sequence_methods(field_name: str):
|
| 76 |
+
def len_method(self) -> int:
|
| 77 |
+
return len(getattr(self, field_name))
|
| 78 |
+
|
| 79 |
+
def iter_method(self):
|
| 80 |
+
return iter(getattr(self, field_name))
|
| 81 |
+
|
| 82 |
+
def getitem_method(self, idx):
|
| 83 |
+
return getattr(self, field_name)[idx]
|
| 84 |
+
|
| 85 |
+
return len_method, iter_method, getitem_method
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class DuplicateExtension(Exception):
|
| 89 |
+
def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
|
| 90 |
+
super().__init__(msg)
|
| 91 |
+
self.oid = oid
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
class ExtensionNotFound(Exception):
|
| 95 |
+
def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
|
| 96 |
+
super().__init__(msg)
|
| 97 |
+
self.oid = oid
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
class ExtensionType(metaclass=abc.ABCMeta):
|
| 101 |
+
oid: typing.ClassVar[ObjectIdentifier]
|
| 102 |
+
|
| 103 |
+
def public_bytes(self) -> bytes:
|
| 104 |
+
"""
|
| 105 |
+
Serializes the extension type to DER.
|
| 106 |
+
"""
|
| 107 |
+
raise NotImplementedError(
|
| 108 |
+
f"public_bytes is not implemented for extension type {self!r}"
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
class Extensions:
|
| 113 |
+
def __init__(self, extensions: Iterable[Extension[ExtensionType]]) -> None:
|
| 114 |
+
self._extensions = list(extensions)
|
| 115 |
+
|
| 116 |
+
def get_extension_for_oid(
|
| 117 |
+
self, oid: ObjectIdentifier
|
| 118 |
+
) -> Extension[ExtensionType]:
|
| 119 |
+
for ext in self:
|
| 120 |
+
if ext.oid == oid:
|
| 121 |
+
return ext
|
| 122 |
+
|
| 123 |
+
raise ExtensionNotFound(f"No {oid} extension was found", oid)
|
| 124 |
+
|
| 125 |
+
def get_extension_for_class(
|
| 126 |
+
self, extclass: type[ExtensionTypeVar]
|
| 127 |
+
) -> Extension[ExtensionTypeVar]:
|
| 128 |
+
if extclass is UnrecognizedExtension:
|
| 129 |
+
raise TypeError(
|
| 130 |
+
"UnrecognizedExtension can't be used with "
|
| 131 |
+
"get_extension_for_class because more than one instance of the"
|
| 132 |
+
" class may be present."
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
for ext in self:
|
| 136 |
+
if isinstance(ext.value, extclass):
|
| 137 |
+
return ext
|
| 138 |
+
|
| 139 |
+
raise ExtensionNotFound(
|
| 140 |
+
f"No {extclass} extension was found", extclass.oid
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_extensions")
|
| 144 |
+
|
| 145 |
+
def __repr__(self) -> str:
|
| 146 |
+
return f"<Extensions({self._extensions})>"
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
class CRLNumber(ExtensionType):
|
| 150 |
+
oid = ExtensionOID.CRL_NUMBER
|
| 151 |
+
|
| 152 |
+
def __init__(self, crl_number: int) -> None:
|
| 153 |
+
if not isinstance(crl_number, int):
|
| 154 |
+
raise TypeError("crl_number must be an integer")
|
| 155 |
+
|
| 156 |
+
self._crl_number = crl_number
|
| 157 |
+
|
| 158 |
+
def __eq__(self, other: object) -> bool:
|
| 159 |
+
if not isinstance(other, CRLNumber):
|
| 160 |
+
return NotImplemented
|
| 161 |
+
|
| 162 |
+
return self.crl_number == other.crl_number
|
| 163 |
+
|
| 164 |
+
def __hash__(self) -> int:
|
| 165 |
+
return hash(self.crl_number)
|
| 166 |
+
|
| 167 |
+
def __repr__(self) -> str:
|
| 168 |
+
return f"<CRLNumber({self.crl_number})>"
|
| 169 |
+
|
| 170 |
+
@property
|
| 171 |
+
def crl_number(self) -> int:
|
| 172 |
+
return self._crl_number
|
| 173 |
+
|
| 174 |
+
def public_bytes(self) -> bytes:
|
| 175 |
+
return rust_x509.encode_extension_value(self)
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
class AuthorityKeyIdentifier(ExtensionType):
|
| 179 |
+
oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER
|
| 180 |
+
|
| 181 |
+
def __init__(
|
| 182 |
+
self,
|
| 183 |
+
key_identifier: bytes | None,
|
| 184 |
+
authority_cert_issuer: Iterable[GeneralName] | None,
|
| 185 |
+
authority_cert_serial_number: int | None,
|
| 186 |
+
) -> None:
|
| 187 |
+
if (authority_cert_issuer is None) != (
|
| 188 |
+
authority_cert_serial_number is None
|
| 189 |
+
):
|
| 190 |
+
raise ValueError(
|
| 191 |
+
"authority_cert_issuer and authority_cert_serial_number "
|
| 192 |
+
"must both be present or both None"
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
if authority_cert_issuer is not None:
|
| 196 |
+
authority_cert_issuer = list(authority_cert_issuer)
|
| 197 |
+
if not all(
|
| 198 |
+
isinstance(x, GeneralName) for x in authority_cert_issuer
|
| 199 |
+
):
|
| 200 |
+
raise TypeError(
|
| 201 |
+
"authority_cert_issuer must be a list of GeneralName "
|
| 202 |
+
"objects"
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
if authority_cert_serial_number is not None and not isinstance(
|
| 206 |
+
authority_cert_serial_number, int
|
| 207 |
+
):
|
| 208 |
+
raise TypeError("authority_cert_serial_number must be an integer")
|
| 209 |
+
|
| 210 |
+
self._key_identifier = key_identifier
|
| 211 |
+
self._authority_cert_issuer = authority_cert_issuer
|
| 212 |
+
self._authority_cert_serial_number = authority_cert_serial_number
|
| 213 |
+
|
| 214 |
+
# This takes a subset of CertificatePublicKeyTypes because an issuer
|
| 215 |
+
# cannot have an X25519/X448 key. This introduces some unfortunate
|
| 216 |
+
# asymmetry that requires typing users to explicitly
|
| 217 |
+
# narrow their type, but we should make this accurate and not just
|
| 218 |
+
# convenient.
|
| 219 |
+
@classmethod
|
| 220 |
+
def from_issuer_public_key(
|
| 221 |
+
cls, public_key: CertificateIssuerPublicKeyTypes
|
| 222 |
+
) -> AuthorityKeyIdentifier:
|
| 223 |
+
digest = _key_identifier_from_public_key(public_key)
|
| 224 |
+
return cls(
|
| 225 |
+
key_identifier=digest,
|
| 226 |
+
authority_cert_issuer=None,
|
| 227 |
+
authority_cert_serial_number=None,
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
@classmethod
|
| 231 |
+
def from_issuer_subject_key_identifier(
|
| 232 |
+
cls, ski: SubjectKeyIdentifier
|
| 233 |
+
) -> AuthorityKeyIdentifier:
|
| 234 |
+
return cls(
|
| 235 |
+
key_identifier=ski.digest,
|
| 236 |
+
authority_cert_issuer=None,
|
| 237 |
+
authority_cert_serial_number=None,
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
def __repr__(self) -> str:
|
| 241 |
+
return (
|
| 242 |
+
f"<AuthorityKeyIdentifier(key_identifier={self.key_identifier!r}, "
|
| 243 |
+
f"authority_cert_issuer={self.authority_cert_issuer}, "
|
| 244 |
+
f"authority_cert_serial_number={self.authority_cert_serial_number}"
|
| 245 |
+
")>"
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
def __eq__(self, other: object) -> bool:
|
| 249 |
+
if not isinstance(other, AuthorityKeyIdentifier):
|
| 250 |
+
return NotImplemented
|
| 251 |
+
|
| 252 |
+
return (
|
| 253 |
+
self.key_identifier == other.key_identifier
|
| 254 |
+
and self.authority_cert_issuer == other.authority_cert_issuer
|
| 255 |
+
and self.authority_cert_serial_number
|
| 256 |
+
== other.authority_cert_serial_number
|
| 257 |
+
)
|
| 258 |
+
|
| 259 |
+
def __hash__(self) -> int:
|
| 260 |
+
if self.authority_cert_issuer is None:
|
| 261 |
+
aci = None
|
| 262 |
+
else:
|
| 263 |
+
aci = tuple(self.authority_cert_issuer)
|
| 264 |
+
return hash(
|
| 265 |
+
(self.key_identifier, aci, self.authority_cert_serial_number)
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
+
@property
|
| 269 |
+
def key_identifier(self) -> bytes | None:
|
| 270 |
+
return self._key_identifier
|
| 271 |
+
|
| 272 |
+
@property
|
| 273 |
+
def authority_cert_issuer(
|
| 274 |
+
self,
|
| 275 |
+
) -> list[GeneralName] | None:
|
| 276 |
+
return self._authority_cert_issuer
|
| 277 |
+
|
| 278 |
+
@property
|
| 279 |
+
def authority_cert_serial_number(self) -> int | None:
|
| 280 |
+
return self._authority_cert_serial_number
|
| 281 |
+
|
| 282 |
+
def public_bytes(self) -> bytes:
|
| 283 |
+
return rust_x509.encode_extension_value(self)
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
class SubjectKeyIdentifier(ExtensionType):
|
| 287 |
+
oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER
|
| 288 |
+
|
| 289 |
+
def __init__(self, digest: bytes) -> None:
|
| 290 |
+
self._digest = digest
|
| 291 |
+
|
| 292 |
+
@classmethod
|
| 293 |
+
def from_public_key(
|
| 294 |
+
cls, public_key: CertificatePublicKeyTypes
|
| 295 |
+
) -> SubjectKeyIdentifier:
|
| 296 |
+
return cls(_key_identifier_from_public_key(public_key))
|
| 297 |
+
|
| 298 |
+
@property
|
| 299 |
+
def digest(self) -> bytes:
|
| 300 |
+
return self._digest
|
| 301 |
+
|
| 302 |
+
@property
|
| 303 |
+
def key_identifier(self) -> bytes:
|
| 304 |
+
return self._digest
|
| 305 |
+
|
| 306 |
+
def __repr__(self) -> str:
|
| 307 |
+
return f"<SubjectKeyIdentifier(digest={self.digest!r})>"
|
| 308 |
+
|
| 309 |
+
def __eq__(self, other: object) -> bool:
|
| 310 |
+
if not isinstance(other, SubjectKeyIdentifier):
|
| 311 |
+
return NotImplemented
|
| 312 |
+
|
| 313 |
+
return constant_time.bytes_eq(self.digest, other.digest)
|
| 314 |
+
|
| 315 |
+
def __hash__(self) -> int:
|
| 316 |
+
return hash(self.digest)
|
| 317 |
+
|
| 318 |
+
def public_bytes(self) -> bytes:
|
| 319 |
+
return rust_x509.encode_extension_value(self)
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
class AuthorityInformationAccess(ExtensionType):
|
| 323 |
+
oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS
|
| 324 |
+
|
| 325 |
+
def __init__(self, descriptions: Iterable[AccessDescription]) -> None:
|
| 326 |
+
descriptions = list(descriptions)
|
| 327 |
+
if not all(isinstance(x, AccessDescription) for x in descriptions):
|
| 328 |
+
raise TypeError(
|
| 329 |
+
"Every item in the descriptions list must be an "
|
| 330 |
+
"AccessDescription"
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
self._descriptions = descriptions
|
| 334 |
+
|
| 335 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")
|
| 336 |
+
|
| 337 |
+
def __repr__(self) -> str:
|
| 338 |
+
return f"<AuthorityInformationAccess({self._descriptions})>"
|
| 339 |
+
|
| 340 |
+
def __eq__(self, other: object) -> bool:
|
| 341 |
+
if not isinstance(other, AuthorityInformationAccess):
|
| 342 |
+
return NotImplemented
|
| 343 |
+
|
| 344 |
+
return self._descriptions == other._descriptions
|
| 345 |
+
|
| 346 |
+
def __hash__(self) -> int:
|
| 347 |
+
return hash(tuple(self._descriptions))
|
| 348 |
+
|
| 349 |
+
def public_bytes(self) -> bytes:
|
| 350 |
+
return rust_x509.encode_extension_value(self)
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
class SubjectInformationAccess(ExtensionType):
|
| 354 |
+
oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS
|
| 355 |
+
|
| 356 |
+
def __init__(self, descriptions: Iterable[AccessDescription]) -> None:
|
| 357 |
+
descriptions = list(descriptions)
|
| 358 |
+
if not all(isinstance(x, AccessDescription) for x in descriptions):
|
| 359 |
+
raise TypeError(
|
| 360 |
+
"Every item in the descriptions list must be an "
|
| 361 |
+
"AccessDescription"
|
| 362 |
+
)
|
| 363 |
+
|
| 364 |
+
self._descriptions = descriptions
|
| 365 |
+
|
| 366 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")
|
| 367 |
+
|
| 368 |
+
def __repr__(self) -> str:
|
| 369 |
+
return f"<SubjectInformationAccess({self._descriptions})>"
|
| 370 |
+
|
| 371 |
+
def __eq__(self, other: object) -> bool:
|
| 372 |
+
if not isinstance(other, SubjectInformationAccess):
|
| 373 |
+
return NotImplemented
|
| 374 |
+
|
| 375 |
+
return self._descriptions == other._descriptions
|
| 376 |
+
|
| 377 |
+
def __hash__(self) -> int:
|
| 378 |
+
return hash(tuple(self._descriptions))
|
| 379 |
+
|
| 380 |
+
def public_bytes(self) -> bytes:
|
| 381 |
+
return rust_x509.encode_extension_value(self)
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
class AccessDescription:
|
| 385 |
+
def __init__(
|
| 386 |
+
self, access_method: ObjectIdentifier, access_location: GeneralName
|
| 387 |
+
) -> None:
|
| 388 |
+
if not isinstance(access_method, ObjectIdentifier):
|
| 389 |
+
raise TypeError("access_method must be an ObjectIdentifier")
|
| 390 |
+
|
| 391 |
+
if not isinstance(access_location, GeneralName):
|
| 392 |
+
raise TypeError("access_location must be a GeneralName")
|
| 393 |
+
|
| 394 |
+
self._access_method = access_method
|
| 395 |
+
self._access_location = access_location
|
| 396 |
+
|
| 397 |
+
def __repr__(self) -> str:
|
| 398 |
+
return (
|
| 399 |
+
f"<AccessDescription(access_method={self.access_method}, "
|
| 400 |
+
f"access_location={self.access_location})>"
|
| 401 |
+
)
|
| 402 |
+
|
| 403 |
+
def __eq__(self, other: object) -> bool:
|
| 404 |
+
if not isinstance(other, AccessDescription):
|
| 405 |
+
return NotImplemented
|
| 406 |
+
|
| 407 |
+
return (
|
| 408 |
+
self.access_method == other.access_method
|
| 409 |
+
and self.access_location == other.access_location
|
| 410 |
+
)
|
| 411 |
+
|
| 412 |
+
def __hash__(self) -> int:
|
| 413 |
+
return hash((self.access_method, self.access_location))
|
| 414 |
+
|
| 415 |
+
@property
|
| 416 |
+
def access_method(self) -> ObjectIdentifier:
|
| 417 |
+
return self._access_method
|
| 418 |
+
|
| 419 |
+
@property
|
| 420 |
+
def access_location(self) -> GeneralName:
|
| 421 |
+
return self._access_location
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
class BasicConstraints(ExtensionType):
|
| 425 |
+
oid = ExtensionOID.BASIC_CONSTRAINTS
|
| 426 |
+
|
| 427 |
+
def __init__(self, ca: bool, path_length: int | None) -> None:
|
| 428 |
+
if not isinstance(ca, bool):
|
| 429 |
+
raise TypeError("ca must be a boolean value")
|
| 430 |
+
|
| 431 |
+
if path_length is not None and not ca:
|
| 432 |
+
raise ValueError("path_length must be None when ca is False")
|
| 433 |
+
|
| 434 |
+
if path_length is not None and (
|
| 435 |
+
not isinstance(path_length, int) or path_length < 0
|
| 436 |
+
):
|
| 437 |
+
raise TypeError(
|
| 438 |
+
"path_length must be a non-negative integer or None"
|
| 439 |
+
)
|
| 440 |
+
|
| 441 |
+
self._ca = ca
|
| 442 |
+
self._path_length = path_length
|
| 443 |
+
|
| 444 |
+
@property
|
| 445 |
+
def ca(self) -> bool:
|
| 446 |
+
return self._ca
|
| 447 |
+
|
| 448 |
+
@property
|
| 449 |
+
def path_length(self) -> int | None:
|
| 450 |
+
return self._path_length
|
| 451 |
+
|
| 452 |
+
def __repr__(self) -> str:
|
| 453 |
+
return (
|
| 454 |
+
f"<BasicConstraints(ca={self.ca}, path_length={self.path_length})>"
|
| 455 |
+
)
|
| 456 |
+
|
| 457 |
+
def __eq__(self, other: object) -> bool:
|
| 458 |
+
if not isinstance(other, BasicConstraints):
|
| 459 |
+
return NotImplemented
|
| 460 |
+
|
| 461 |
+
return self.ca == other.ca and self.path_length == other.path_length
|
| 462 |
+
|
| 463 |
+
def __hash__(self) -> int:
|
| 464 |
+
return hash((self.ca, self.path_length))
|
| 465 |
+
|
| 466 |
+
def public_bytes(self) -> bytes:
|
| 467 |
+
return rust_x509.encode_extension_value(self)
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
class DeltaCRLIndicator(ExtensionType):
|
| 471 |
+
oid = ExtensionOID.DELTA_CRL_INDICATOR
|
| 472 |
+
|
| 473 |
+
def __init__(self, crl_number: int) -> None:
|
| 474 |
+
if not isinstance(crl_number, int):
|
| 475 |
+
raise TypeError("crl_number must be an integer")
|
| 476 |
+
|
| 477 |
+
self._crl_number = crl_number
|
| 478 |
+
|
| 479 |
+
@property
|
| 480 |
+
def crl_number(self) -> int:
|
| 481 |
+
return self._crl_number
|
| 482 |
+
|
| 483 |
+
def __eq__(self, other: object) -> bool:
|
| 484 |
+
if not isinstance(other, DeltaCRLIndicator):
|
| 485 |
+
return NotImplemented
|
| 486 |
+
|
| 487 |
+
return self.crl_number == other.crl_number
|
| 488 |
+
|
| 489 |
+
def __hash__(self) -> int:
|
| 490 |
+
return hash(self.crl_number)
|
| 491 |
+
|
| 492 |
+
def __repr__(self) -> str:
|
| 493 |
+
return f"<DeltaCRLIndicator(crl_number={self.crl_number})>"
|
| 494 |
+
|
| 495 |
+
def public_bytes(self) -> bytes:
|
| 496 |
+
return rust_x509.encode_extension_value(self)
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
class CRLDistributionPoints(ExtensionType):
|
| 500 |
+
oid = ExtensionOID.CRL_DISTRIBUTION_POINTS
|
| 501 |
+
|
| 502 |
+
def __init__(
|
| 503 |
+
self, distribution_points: Iterable[DistributionPoint]
|
| 504 |
+
) -> None:
|
| 505 |
+
distribution_points = list(distribution_points)
|
| 506 |
+
if not all(
|
| 507 |
+
isinstance(x, DistributionPoint) for x in distribution_points
|
| 508 |
+
):
|
| 509 |
+
raise TypeError(
|
| 510 |
+
"distribution_points must be a list of DistributionPoint "
|
| 511 |
+
"objects"
|
| 512 |
+
)
|
| 513 |
+
|
| 514 |
+
self._distribution_points = distribution_points
|
| 515 |
+
|
| 516 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods(
|
| 517 |
+
"_distribution_points"
|
| 518 |
+
)
|
| 519 |
+
|
| 520 |
+
def __repr__(self) -> str:
|
| 521 |
+
return f"<CRLDistributionPoints({self._distribution_points})>"
|
| 522 |
+
|
| 523 |
+
def __eq__(self, other: object) -> bool:
|
| 524 |
+
if not isinstance(other, CRLDistributionPoints):
|
| 525 |
+
return NotImplemented
|
| 526 |
+
|
| 527 |
+
return self._distribution_points == other._distribution_points
|
| 528 |
+
|
| 529 |
+
def __hash__(self) -> int:
|
| 530 |
+
return hash(tuple(self._distribution_points))
|
| 531 |
+
|
| 532 |
+
def public_bytes(self) -> bytes:
|
| 533 |
+
return rust_x509.encode_extension_value(self)
|
| 534 |
+
|
| 535 |
+
|
| 536 |
+
class FreshestCRL(ExtensionType):
|
| 537 |
+
oid = ExtensionOID.FRESHEST_CRL
|
| 538 |
+
|
| 539 |
+
def __init__(
|
| 540 |
+
self, distribution_points: Iterable[DistributionPoint]
|
| 541 |
+
) -> None:
|
| 542 |
+
distribution_points = list(distribution_points)
|
| 543 |
+
if not all(
|
| 544 |
+
isinstance(x, DistributionPoint) for x in distribution_points
|
| 545 |
+
):
|
| 546 |
+
raise TypeError(
|
| 547 |
+
"distribution_points must be a list of DistributionPoint "
|
| 548 |
+
"objects"
|
| 549 |
+
)
|
| 550 |
+
|
| 551 |
+
self._distribution_points = distribution_points
|
| 552 |
+
|
| 553 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods(
|
| 554 |
+
"_distribution_points"
|
| 555 |
+
)
|
| 556 |
+
|
| 557 |
+
def __repr__(self) -> str:
|
| 558 |
+
return f"<FreshestCRL({self._distribution_points})>"
|
| 559 |
+
|
| 560 |
+
def __eq__(self, other: object) -> bool:
|
| 561 |
+
if not isinstance(other, FreshestCRL):
|
| 562 |
+
return NotImplemented
|
| 563 |
+
|
| 564 |
+
return self._distribution_points == other._distribution_points
|
| 565 |
+
|
| 566 |
+
def __hash__(self) -> int:
|
| 567 |
+
return hash(tuple(self._distribution_points))
|
| 568 |
+
|
| 569 |
+
def public_bytes(self) -> bytes:
|
| 570 |
+
return rust_x509.encode_extension_value(self)
|
| 571 |
+
|
| 572 |
+
|
| 573 |
+
class DistributionPoint:
|
| 574 |
+
def __init__(
|
| 575 |
+
self,
|
| 576 |
+
full_name: Iterable[GeneralName] | None,
|
| 577 |
+
relative_name: RelativeDistinguishedName | None,
|
| 578 |
+
reasons: frozenset[ReasonFlags] | None,
|
| 579 |
+
crl_issuer: Iterable[GeneralName] | None,
|
| 580 |
+
) -> None:
|
| 581 |
+
if full_name and relative_name:
|
| 582 |
+
raise ValueError(
|
| 583 |
+
"You cannot provide both full_name and relative_name, at "
|
| 584 |
+
"least one must be None."
|
| 585 |
+
)
|
| 586 |
+
if not full_name and not relative_name and not crl_issuer:
|
| 587 |
+
raise ValueError(
|
| 588 |
+
"Either full_name, relative_name or crl_issuer must be "
|
| 589 |
+
"provided."
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
if full_name is not None:
|
| 593 |
+
full_name = list(full_name)
|
| 594 |
+
if not all(isinstance(x, GeneralName) for x in full_name):
|
| 595 |
+
raise TypeError(
|
| 596 |
+
"full_name must be a list of GeneralName objects"
|
| 597 |
+
)
|
| 598 |
+
|
| 599 |
+
if relative_name:
|
| 600 |
+
if not isinstance(relative_name, RelativeDistinguishedName):
|
| 601 |
+
raise TypeError(
|
| 602 |
+
"relative_name must be a RelativeDistinguishedName"
|
| 603 |
+
)
|
| 604 |
+
|
| 605 |
+
if crl_issuer is not None:
|
| 606 |
+
crl_issuer = list(crl_issuer)
|
| 607 |
+
if not all(isinstance(x, GeneralName) for x in crl_issuer):
|
| 608 |
+
raise TypeError(
|
| 609 |
+
"crl_issuer must be None or a list of general names"
|
| 610 |
+
)
|
| 611 |
+
|
| 612 |
+
if reasons and (
|
| 613 |
+
not isinstance(reasons, frozenset)
|
| 614 |
+
or not all(isinstance(x, ReasonFlags) for x in reasons)
|
| 615 |
+
):
|
| 616 |
+
raise TypeError("reasons must be None or frozenset of ReasonFlags")
|
| 617 |
+
|
| 618 |
+
if reasons and (
|
| 619 |
+
ReasonFlags.unspecified in reasons
|
| 620 |
+
or ReasonFlags.remove_from_crl in reasons
|
| 621 |
+
):
|
| 622 |
+
raise ValueError(
|
| 623 |
+
"unspecified and remove_from_crl are not valid reasons in a "
|
| 624 |
+
"DistributionPoint"
|
| 625 |
+
)
|
| 626 |
+
|
| 627 |
+
self._full_name = full_name
|
| 628 |
+
self._relative_name = relative_name
|
| 629 |
+
self._reasons = reasons
|
| 630 |
+
self._crl_issuer = crl_issuer
|
| 631 |
+
|
| 632 |
+
def __repr__(self) -> str:
|
| 633 |
+
return (
|
| 634 |
+
"<DistributionPoint(full_name={0.full_name}, relative_name={0.rela"
|
| 635 |
+
"tive_name}, reasons={0.reasons}, "
|
| 636 |
+
"crl_issuer={0.crl_issuer})>".format(self)
|
| 637 |
+
)
|
| 638 |
+
|
| 639 |
+
def __eq__(self, other: object) -> bool:
|
| 640 |
+
if not isinstance(other, DistributionPoint):
|
| 641 |
+
return NotImplemented
|
| 642 |
+
|
| 643 |
+
return (
|
| 644 |
+
self.full_name == other.full_name
|
| 645 |
+
and self.relative_name == other.relative_name
|
| 646 |
+
and self.reasons == other.reasons
|
| 647 |
+
and self.crl_issuer == other.crl_issuer
|
| 648 |
+
)
|
| 649 |
+
|
| 650 |
+
def __hash__(self) -> int:
|
| 651 |
+
if self.full_name is not None:
|
| 652 |
+
fn: tuple[GeneralName, ...] | None = tuple(self.full_name)
|
| 653 |
+
else:
|
| 654 |
+
fn = None
|
| 655 |
+
|
| 656 |
+
if self.crl_issuer is not None:
|
| 657 |
+
crl_issuer: tuple[GeneralName, ...] | None = tuple(self.crl_issuer)
|
| 658 |
+
else:
|
| 659 |
+
crl_issuer = None
|
| 660 |
+
|
| 661 |
+
return hash((fn, self.relative_name, self.reasons, crl_issuer))
|
| 662 |
+
|
| 663 |
+
@property
|
| 664 |
+
def full_name(self) -> list[GeneralName] | None:
|
| 665 |
+
return self._full_name
|
| 666 |
+
|
| 667 |
+
@property
|
| 668 |
+
def relative_name(self) -> RelativeDistinguishedName | None:
|
| 669 |
+
return self._relative_name
|
| 670 |
+
|
| 671 |
+
@property
|
| 672 |
+
def reasons(self) -> frozenset[ReasonFlags] | None:
|
| 673 |
+
return self._reasons
|
| 674 |
+
|
| 675 |
+
@property
|
| 676 |
+
def crl_issuer(self) -> list[GeneralName] | None:
|
| 677 |
+
return self._crl_issuer
|
| 678 |
+
|
| 679 |
+
|
| 680 |
+
class ReasonFlags(utils.Enum):
|
| 681 |
+
unspecified = "unspecified"
|
| 682 |
+
key_compromise = "keyCompromise"
|
| 683 |
+
ca_compromise = "cACompromise"
|
| 684 |
+
affiliation_changed = "affiliationChanged"
|
| 685 |
+
superseded = "superseded"
|
| 686 |
+
cessation_of_operation = "cessationOfOperation"
|
| 687 |
+
certificate_hold = "certificateHold"
|
| 688 |
+
privilege_withdrawn = "privilegeWithdrawn"
|
| 689 |
+
aa_compromise = "aACompromise"
|
| 690 |
+
remove_from_crl = "removeFromCRL"
|
| 691 |
+
|
| 692 |
+
|
| 693 |
+
# These are distribution point bit string mappings. Not to be confused with
|
| 694 |
+
# CRLReason reason flags bit string mappings.
|
| 695 |
+
# ReasonFlags ::= BIT STRING {
|
| 696 |
+
# unused (0),
|
| 697 |
+
# keyCompromise (1),
|
| 698 |
+
# cACompromise (2),
|
| 699 |
+
# affiliationChanged (3),
|
| 700 |
+
# superseded (4),
|
| 701 |
+
# cessationOfOperation (5),
|
| 702 |
+
# certificateHold (6),
|
| 703 |
+
# privilegeWithdrawn (7),
|
| 704 |
+
# aACompromise (8) }
|
| 705 |
+
_REASON_BIT_MAPPING = {
|
| 706 |
+
1: ReasonFlags.key_compromise,
|
| 707 |
+
2: ReasonFlags.ca_compromise,
|
| 708 |
+
3: ReasonFlags.affiliation_changed,
|
| 709 |
+
4: ReasonFlags.superseded,
|
| 710 |
+
5: ReasonFlags.cessation_of_operation,
|
| 711 |
+
6: ReasonFlags.certificate_hold,
|
| 712 |
+
7: ReasonFlags.privilege_withdrawn,
|
| 713 |
+
8: ReasonFlags.aa_compromise,
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
_CRLREASONFLAGS = {
|
| 717 |
+
ReasonFlags.key_compromise: 1,
|
| 718 |
+
ReasonFlags.ca_compromise: 2,
|
| 719 |
+
ReasonFlags.affiliation_changed: 3,
|
| 720 |
+
ReasonFlags.superseded: 4,
|
| 721 |
+
ReasonFlags.cessation_of_operation: 5,
|
| 722 |
+
ReasonFlags.certificate_hold: 6,
|
| 723 |
+
ReasonFlags.privilege_withdrawn: 7,
|
| 724 |
+
ReasonFlags.aa_compromise: 8,
|
| 725 |
+
}
|
| 726 |
+
|
| 727 |
+
# CRLReason ::= ENUMERATED {
|
| 728 |
+
# unspecified (0),
|
| 729 |
+
# keyCompromise (1),
|
| 730 |
+
# cACompromise (2),
|
| 731 |
+
# affiliationChanged (3),
|
| 732 |
+
# superseded (4),
|
| 733 |
+
# cessationOfOperation (5),
|
| 734 |
+
# certificateHold (6),
|
| 735 |
+
# -- value 7 is not used
|
| 736 |
+
# removeFromCRL (8),
|
| 737 |
+
# privilegeWithdrawn (9),
|
| 738 |
+
# aACompromise (10) }
|
| 739 |
+
_CRL_ENTRY_REASON_ENUM_TO_CODE = {
|
| 740 |
+
ReasonFlags.unspecified: 0,
|
| 741 |
+
ReasonFlags.key_compromise: 1,
|
| 742 |
+
ReasonFlags.ca_compromise: 2,
|
| 743 |
+
ReasonFlags.affiliation_changed: 3,
|
| 744 |
+
ReasonFlags.superseded: 4,
|
| 745 |
+
ReasonFlags.cessation_of_operation: 5,
|
| 746 |
+
ReasonFlags.certificate_hold: 6,
|
| 747 |
+
ReasonFlags.remove_from_crl: 8,
|
| 748 |
+
ReasonFlags.privilege_withdrawn: 9,
|
| 749 |
+
ReasonFlags.aa_compromise: 10,
|
| 750 |
+
}
|
| 751 |
+
|
| 752 |
+
|
| 753 |
+
class PolicyConstraints(ExtensionType):
|
| 754 |
+
oid = ExtensionOID.POLICY_CONSTRAINTS
|
| 755 |
+
|
| 756 |
+
def __init__(
|
| 757 |
+
self,
|
| 758 |
+
require_explicit_policy: int | None,
|
| 759 |
+
inhibit_policy_mapping: int | None,
|
| 760 |
+
) -> None:
|
| 761 |
+
if require_explicit_policy is not None and not isinstance(
|
| 762 |
+
require_explicit_policy, int
|
| 763 |
+
):
|
| 764 |
+
raise TypeError(
|
| 765 |
+
"require_explicit_policy must be a non-negative integer or "
|
| 766 |
+
"None"
|
| 767 |
+
)
|
| 768 |
+
|
| 769 |
+
if inhibit_policy_mapping is not None and not isinstance(
|
| 770 |
+
inhibit_policy_mapping, int
|
| 771 |
+
):
|
| 772 |
+
raise TypeError(
|
| 773 |
+
"inhibit_policy_mapping must be a non-negative integer or None"
|
| 774 |
+
)
|
| 775 |
+
|
| 776 |
+
if inhibit_policy_mapping is None and require_explicit_policy is None:
|
| 777 |
+
raise ValueError(
|
| 778 |
+
"At least one of require_explicit_policy and "
|
| 779 |
+
"inhibit_policy_mapping must not be None"
|
| 780 |
+
)
|
| 781 |
+
|
| 782 |
+
self._require_explicit_policy = require_explicit_policy
|
| 783 |
+
self._inhibit_policy_mapping = inhibit_policy_mapping
|
| 784 |
+
|
| 785 |
+
def __repr__(self) -> str:
|
| 786 |
+
return (
|
| 787 |
+
"<PolicyConstraints(require_explicit_policy={0.require_explicit"
|
| 788 |
+
"_policy}, inhibit_policy_mapping={0.inhibit_policy_"
|
| 789 |
+
"mapping})>".format(self)
|
| 790 |
+
)
|
| 791 |
+
|
| 792 |
+
def __eq__(self, other: object) -> bool:
|
| 793 |
+
if not isinstance(other, PolicyConstraints):
|
| 794 |
+
return NotImplemented
|
| 795 |
+
|
| 796 |
+
return (
|
| 797 |
+
self.require_explicit_policy == other.require_explicit_policy
|
| 798 |
+
and self.inhibit_policy_mapping == other.inhibit_policy_mapping
|
| 799 |
+
)
|
| 800 |
+
|
| 801 |
+
def __hash__(self) -> int:
|
| 802 |
+
return hash(
|
| 803 |
+
(self.require_explicit_policy, self.inhibit_policy_mapping)
|
| 804 |
+
)
|
| 805 |
+
|
| 806 |
+
@property
|
| 807 |
+
def require_explicit_policy(self) -> int | None:
|
| 808 |
+
return self._require_explicit_policy
|
| 809 |
+
|
| 810 |
+
@property
|
| 811 |
+
def inhibit_policy_mapping(self) -> int | None:
|
| 812 |
+
return self._inhibit_policy_mapping
|
| 813 |
+
|
| 814 |
+
def public_bytes(self) -> bytes:
|
| 815 |
+
return rust_x509.encode_extension_value(self)
|
| 816 |
+
|
| 817 |
+
|
| 818 |
+
class CertificatePolicies(ExtensionType):
|
| 819 |
+
oid = ExtensionOID.CERTIFICATE_POLICIES
|
| 820 |
+
|
| 821 |
+
def __init__(self, policies: Iterable[PolicyInformation]) -> None:
|
| 822 |
+
policies = list(policies)
|
| 823 |
+
if not all(isinstance(x, PolicyInformation) for x in policies):
|
| 824 |
+
raise TypeError(
|
| 825 |
+
"Every item in the policies list must be a PolicyInformation"
|
| 826 |
+
)
|
| 827 |
+
|
| 828 |
+
self._policies = policies
|
| 829 |
+
|
| 830 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_policies")
|
| 831 |
+
|
| 832 |
+
def __repr__(self) -> str:
|
| 833 |
+
return f"<CertificatePolicies({self._policies})>"
|
| 834 |
+
|
| 835 |
+
def __eq__(self, other: object) -> bool:
|
| 836 |
+
if not isinstance(other, CertificatePolicies):
|
| 837 |
+
return NotImplemented
|
| 838 |
+
|
| 839 |
+
return self._policies == other._policies
|
| 840 |
+
|
| 841 |
+
def __hash__(self) -> int:
|
| 842 |
+
return hash(tuple(self._policies))
|
| 843 |
+
|
| 844 |
+
def public_bytes(self) -> bytes:
|
| 845 |
+
return rust_x509.encode_extension_value(self)
|
| 846 |
+
|
| 847 |
+
|
| 848 |
+
class PolicyInformation:
|
| 849 |
+
def __init__(
|
| 850 |
+
self,
|
| 851 |
+
policy_identifier: ObjectIdentifier,
|
| 852 |
+
policy_qualifiers: Iterable[str | UserNotice] | None,
|
| 853 |
+
) -> None:
|
| 854 |
+
if not isinstance(policy_identifier, ObjectIdentifier):
|
| 855 |
+
raise TypeError("policy_identifier must be an ObjectIdentifier")
|
| 856 |
+
|
| 857 |
+
self._policy_identifier = policy_identifier
|
| 858 |
+
|
| 859 |
+
if policy_qualifiers is not None:
|
| 860 |
+
policy_qualifiers = list(policy_qualifiers)
|
| 861 |
+
if not all(
|
| 862 |
+
isinstance(x, (str, UserNotice)) for x in policy_qualifiers
|
| 863 |
+
):
|
| 864 |
+
raise TypeError(
|
| 865 |
+
"policy_qualifiers must be a list of strings and/or "
|
| 866 |
+
"UserNotice objects or None"
|
| 867 |
+
)
|
| 868 |
+
|
| 869 |
+
self._policy_qualifiers = policy_qualifiers
|
| 870 |
+
|
| 871 |
+
def __repr__(self) -> str:
|
| 872 |
+
return (
|
| 873 |
+
f"<PolicyInformation(policy_identifier={self.policy_identifier}, "
|
| 874 |
+
f"policy_qualifiers={self.policy_qualifiers})>"
|
| 875 |
+
)
|
| 876 |
+
|
| 877 |
+
def __eq__(self, other: object) -> bool:
|
| 878 |
+
if not isinstance(other, PolicyInformation):
|
| 879 |
+
return NotImplemented
|
| 880 |
+
|
| 881 |
+
return (
|
| 882 |
+
self.policy_identifier == other.policy_identifier
|
| 883 |
+
and self.policy_qualifiers == other.policy_qualifiers
|
| 884 |
+
)
|
| 885 |
+
|
| 886 |
+
def __hash__(self) -> int:
|
| 887 |
+
if self.policy_qualifiers is not None:
|
| 888 |
+
pq = tuple(self.policy_qualifiers)
|
| 889 |
+
else:
|
| 890 |
+
pq = None
|
| 891 |
+
|
| 892 |
+
return hash((self.policy_identifier, pq))
|
| 893 |
+
|
| 894 |
+
@property
|
| 895 |
+
def policy_identifier(self) -> ObjectIdentifier:
|
| 896 |
+
return self._policy_identifier
|
| 897 |
+
|
| 898 |
+
@property
|
| 899 |
+
def policy_qualifiers(
|
| 900 |
+
self,
|
| 901 |
+
) -> list[str | UserNotice] | None:
|
| 902 |
+
return self._policy_qualifiers
|
| 903 |
+
|
| 904 |
+
|
| 905 |
+
class UserNotice:
|
| 906 |
+
def __init__(
|
| 907 |
+
self,
|
| 908 |
+
notice_reference: NoticeReference | None,
|
| 909 |
+
explicit_text: str | None,
|
| 910 |
+
) -> None:
|
| 911 |
+
if notice_reference and not isinstance(
|
| 912 |
+
notice_reference, NoticeReference
|
| 913 |
+
):
|
| 914 |
+
raise TypeError(
|
| 915 |
+
"notice_reference must be None or a NoticeReference"
|
| 916 |
+
)
|
| 917 |
+
|
| 918 |
+
self._notice_reference = notice_reference
|
| 919 |
+
self._explicit_text = explicit_text
|
| 920 |
+
|
| 921 |
+
def __repr__(self) -> str:
|
| 922 |
+
return (
|
| 923 |
+
f"<UserNotice(notice_reference={self.notice_reference}, "
|
| 924 |
+
f"explicit_text={self.explicit_text!r})>"
|
| 925 |
+
)
|
| 926 |
+
|
| 927 |
+
def __eq__(self, other: object) -> bool:
|
| 928 |
+
if not isinstance(other, UserNotice):
|
| 929 |
+
return NotImplemented
|
| 930 |
+
|
| 931 |
+
return (
|
| 932 |
+
self.notice_reference == other.notice_reference
|
| 933 |
+
and self.explicit_text == other.explicit_text
|
| 934 |
+
)
|
| 935 |
+
|
| 936 |
+
def __hash__(self) -> int:
|
| 937 |
+
return hash((self.notice_reference, self.explicit_text))
|
| 938 |
+
|
| 939 |
+
@property
|
| 940 |
+
def notice_reference(self) -> NoticeReference | None:
|
| 941 |
+
return self._notice_reference
|
| 942 |
+
|
| 943 |
+
@property
|
| 944 |
+
def explicit_text(self) -> str | None:
|
| 945 |
+
return self._explicit_text
|
| 946 |
+
|
| 947 |
+
|
| 948 |
+
class NoticeReference:
|
| 949 |
+
def __init__(
|
| 950 |
+
self,
|
| 951 |
+
organization: str | None,
|
| 952 |
+
notice_numbers: Iterable[int],
|
| 953 |
+
) -> None:
|
| 954 |
+
self._organization = organization
|
| 955 |
+
notice_numbers = list(notice_numbers)
|
| 956 |
+
if not all(isinstance(x, int) for x in notice_numbers):
|
| 957 |
+
raise TypeError("notice_numbers must be a list of integers")
|
| 958 |
+
|
| 959 |
+
self._notice_numbers = notice_numbers
|
| 960 |
+
|
| 961 |
+
def __repr__(self) -> str:
|
| 962 |
+
return (
|
| 963 |
+
f"<NoticeReference(organization={self.organization!r}, "
|
| 964 |
+
f"notice_numbers={self.notice_numbers})>"
|
| 965 |
+
)
|
| 966 |
+
|
| 967 |
+
def __eq__(self, other: object) -> bool:
|
| 968 |
+
if not isinstance(other, NoticeReference):
|
| 969 |
+
return NotImplemented
|
| 970 |
+
|
| 971 |
+
return (
|
| 972 |
+
self.organization == other.organization
|
| 973 |
+
and self.notice_numbers == other.notice_numbers
|
| 974 |
+
)
|
| 975 |
+
|
| 976 |
+
def __hash__(self) -> int:
|
| 977 |
+
return hash((self.organization, tuple(self.notice_numbers)))
|
| 978 |
+
|
| 979 |
+
@property
|
| 980 |
+
def organization(self) -> str | None:
|
| 981 |
+
return self._organization
|
| 982 |
+
|
| 983 |
+
@property
|
| 984 |
+
def notice_numbers(self) -> list[int]:
|
| 985 |
+
return self._notice_numbers
|
| 986 |
+
|
| 987 |
+
|
| 988 |
+
class ExtendedKeyUsage(ExtensionType):
|
| 989 |
+
oid = ExtensionOID.EXTENDED_KEY_USAGE
|
| 990 |
+
|
| 991 |
+
def __init__(self, usages: Iterable[ObjectIdentifier]) -> None:
|
| 992 |
+
usages = list(usages)
|
| 993 |
+
if not all(isinstance(x, ObjectIdentifier) for x in usages):
|
| 994 |
+
raise TypeError(
|
| 995 |
+
"Every item in the usages list must be an ObjectIdentifier"
|
| 996 |
+
)
|
| 997 |
+
|
| 998 |
+
self._usages = usages
|
| 999 |
+
|
| 1000 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_usages")
|
| 1001 |
+
|
| 1002 |
+
def __repr__(self) -> str:
|
| 1003 |
+
return f"<ExtendedKeyUsage({self._usages})>"
|
| 1004 |
+
|
| 1005 |
+
def __eq__(self, other: object) -> bool:
|
| 1006 |
+
if not isinstance(other, ExtendedKeyUsage):
|
| 1007 |
+
return NotImplemented
|
| 1008 |
+
|
| 1009 |
+
return self._usages == other._usages
|
| 1010 |
+
|
| 1011 |
+
def __hash__(self) -> int:
|
| 1012 |
+
return hash(tuple(self._usages))
|
| 1013 |
+
|
| 1014 |
+
def public_bytes(self) -> bytes:
|
| 1015 |
+
return rust_x509.encode_extension_value(self)
|
| 1016 |
+
|
| 1017 |
+
|
| 1018 |
+
class OCSPNoCheck(ExtensionType):
|
| 1019 |
+
oid = ExtensionOID.OCSP_NO_CHECK
|
| 1020 |
+
|
| 1021 |
+
def __eq__(self, other: object) -> bool:
|
| 1022 |
+
if not isinstance(other, OCSPNoCheck):
|
| 1023 |
+
return NotImplemented
|
| 1024 |
+
|
| 1025 |
+
return True
|
| 1026 |
+
|
| 1027 |
+
def __hash__(self) -> int:
|
| 1028 |
+
return hash(OCSPNoCheck)
|
| 1029 |
+
|
| 1030 |
+
def __repr__(self) -> str:
|
| 1031 |
+
return "<OCSPNoCheck()>"
|
| 1032 |
+
|
| 1033 |
+
def public_bytes(self) -> bytes:
|
| 1034 |
+
return rust_x509.encode_extension_value(self)
|
| 1035 |
+
|
| 1036 |
+
|
| 1037 |
+
class PrecertPoison(ExtensionType):
|
| 1038 |
+
oid = ExtensionOID.PRECERT_POISON
|
| 1039 |
+
|
| 1040 |
+
def __eq__(self, other: object) -> bool:
|
| 1041 |
+
if not isinstance(other, PrecertPoison):
|
| 1042 |
+
return NotImplemented
|
| 1043 |
+
|
| 1044 |
+
return True
|
| 1045 |
+
|
| 1046 |
+
def __hash__(self) -> int:
|
| 1047 |
+
return hash(PrecertPoison)
|
| 1048 |
+
|
| 1049 |
+
def __repr__(self) -> str:
|
| 1050 |
+
return "<PrecertPoison()>"
|
| 1051 |
+
|
| 1052 |
+
def public_bytes(self) -> bytes:
|
| 1053 |
+
return rust_x509.encode_extension_value(self)
|
| 1054 |
+
|
| 1055 |
+
|
| 1056 |
+
class TLSFeature(ExtensionType):
|
| 1057 |
+
oid = ExtensionOID.TLS_FEATURE
|
| 1058 |
+
|
| 1059 |
+
def __init__(self, features: Iterable[TLSFeatureType]) -> None:
|
| 1060 |
+
features = list(features)
|
| 1061 |
+
if (
|
| 1062 |
+
not all(isinstance(x, TLSFeatureType) for x in features)
|
| 1063 |
+
or len(features) == 0
|
| 1064 |
+
):
|
| 1065 |
+
raise TypeError(
|
| 1066 |
+
"features must be a list of elements from the TLSFeatureType "
|
| 1067 |
+
"enum"
|
| 1068 |
+
)
|
| 1069 |
+
|
| 1070 |
+
self._features = features
|
| 1071 |
+
|
| 1072 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_features")
|
| 1073 |
+
|
| 1074 |
+
def __repr__(self) -> str:
|
| 1075 |
+
return f"<TLSFeature(features={self._features})>"
|
| 1076 |
+
|
| 1077 |
+
def __eq__(self, other: object) -> bool:
|
| 1078 |
+
if not isinstance(other, TLSFeature):
|
| 1079 |
+
return NotImplemented
|
| 1080 |
+
|
| 1081 |
+
return self._features == other._features
|
| 1082 |
+
|
| 1083 |
+
def __hash__(self) -> int:
|
| 1084 |
+
return hash(tuple(self._features))
|
| 1085 |
+
|
| 1086 |
+
def public_bytes(self) -> bytes:
|
| 1087 |
+
return rust_x509.encode_extension_value(self)
|
| 1088 |
+
|
| 1089 |
+
|
| 1090 |
+
class TLSFeatureType(utils.Enum):
|
| 1091 |
+
# status_request is defined in RFC 6066 and is used for what is commonly
|
| 1092 |
+
# called OCSP Must-Staple when present in the TLS Feature extension in an
|
| 1093 |
+
# X.509 certificate.
|
| 1094 |
+
status_request = 5
|
| 1095 |
+
# status_request_v2 is defined in RFC 6961 and allows multiple OCSP
|
| 1096 |
+
# responses to be provided. It is not currently in use by clients or
|
| 1097 |
+
# servers.
|
| 1098 |
+
status_request_v2 = 17
|
| 1099 |
+
|
| 1100 |
+
|
| 1101 |
+
_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType}
|
| 1102 |
+
|
| 1103 |
+
|
| 1104 |
+
class InhibitAnyPolicy(ExtensionType):
|
| 1105 |
+
oid = ExtensionOID.INHIBIT_ANY_POLICY
|
| 1106 |
+
|
| 1107 |
+
def __init__(self, skip_certs: int) -> None:
|
| 1108 |
+
if not isinstance(skip_certs, int):
|
| 1109 |
+
raise TypeError("skip_certs must be an integer")
|
| 1110 |
+
|
| 1111 |
+
if skip_certs < 0:
|
| 1112 |
+
raise ValueError("skip_certs must be a non-negative integer")
|
| 1113 |
+
|
| 1114 |
+
self._skip_certs = skip_certs
|
| 1115 |
+
|
| 1116 |
+
def __repr__(self) -> str:
|
| 1117 |
+
return f"<InhibitAnyPolicy(skip_certs={self.skip_certs})>"
|
| 1118 |
+
|
| 1119 |
+
def __eq__(self, other: object) -> bool:
|
| 1120 |
+
if not isinstance(other, InhibitAnyPolicy):
|
| 1121 |
+
return NotImplemented
|
| 1122 |
+
|
| 1123 |
+
return self.skip_certs == other.skip_certs
|
| 1124 |
+
|
| 1125 |
+
def __hash__(self) -> int:
|
| 1126 |
+
return hash(self.skip_certs)
|
| 1127 |
+
|
| 1128 |
+
@property
|
| 1129 |
+
def skip_certs(self) -> int:
|
| 1130 |
+
return self._skip_certs
|
| 1131 |
+
|
| 1132 |
+
def public_bytes(self) -> bytes:
|
| 1133 |
+
return rust_x509.encode_extension_value(self)
|
| 1134 |
+
|
| 1135 |
+
|
| 1136 |
+
class KeyUsage(ExtensionType):
|
| 1137 |
+
oid = ExtensionOID.KEY_USAGE
|
| 1138 |
+
|
| 1139 |
+
def __init__(
|
| 1140 |
+
self,
|
| 1141 |
+
digital_signature: bool,
|
| 1142 |
+
content_commitment: bool,
|
| 1143 |
+
key_encipherment: bool,
|
| 1144 |
+
data_encipherment: bool,
|
| 1145 |
+
key_agreement: bool,
|
| 1146 |
+
key_cert_sign: bool,
|
| 1147 |
+
crl_sign: bool,
|
| 1148 |
+
encipher_only: bool,
|
| 1149 |
+
decipher_only: bool,
|
| 1150 |
+
) -> None:
|
| 1151 |
+
if not key_agreement and (encipher_only or decipher_only):
|
| 1152 |
+
raise ValueError(
|
| 1153 |
+
"encipher_only and decipher_only can only be true when "
|
| 1154 |
+
"key_agreement is true"
|
| 1155 |
+
)
|
| 1156 |
+
|
| 1157 |
+
self._digital_signature = digital_signature
|
| 1158 |
+
self._content_commitment = content_commitment
|
| 1159 |
+
self._key_encipherment = key_encipherment
|
| 1160 |
+
self._data_encipherment = data_encipherment
|
| 1161 |
+
self._key_agreement = key_agreement
|
| 1162 |
+
self._key_cert_sign = key_cert_sign
|
| 1163 |
+
self._crl_sign = crl_sign
|
| 1164 |
+
self._encipher_only = encipher_only
|
| 1165 |
+
self._decipher_only = decipher_only
|
| 1166 |
+
|
| 1167 |
+
@property
|
| 1168 |
+
def digital_signature(self) -> bool:
|
| 1169 |
+
return self._digital_signature
|
| 1170 |
+
|
| 1171 |
+
@property
|
| 1172 |
+
def content_commitment(self) -> bool:
|
| 1173 |
+
return self._content_commitment
|
| 1174 |
+
|
| 1175 |
+
@property
|
| 1176 |
+
def key_encipherment(self) -> bool:
|
| 1177 |
+
return self._key_encipherment
|
| 1178 |
+
|
| 1179 |
+
@property
|
| 1180 |
+
def data_encipherment(self) -> bool:
|
| 1181 |
+
return self._data_encipherment
|
| 1182 |
+
|
| 1183 |
+
@property
|
| 1184 |
+
def key_agreement(self) -> bool:
|
| 1185 |
+
return self._key_agreement
|
| 1186 |
+
|
| 1187 |
+
@property
|
| 1188 |
+
def key_cert_sign(self) -> bool:
|
| 1189 |
+
return self._key_cert_sign
|
| 1190 |
+
|
| 1191 |
+
@property
|
| 1192 |
+
def crl_sign(self) -> bool:
|
| 1193 |
+
return self._crl_sign
|
| 1194 |
+
|
| 1195 |
+
@property
|
| 1196 |
+
def encipher_only(self) -> bool:
|
| 1197 |
+
if not self.key_agreement:
|
| 1198 |
+
raise ValueError(
|
| 1199 |
+
"encipher_only is undefined unless key_agreement is true"
|
| 1200 |
+
)
|
| 1201 |
+
else:
|
| 1202 |
+
return self._encipher_only
|
| 1203 |
+
|
| 1204 |
+
@property
|
| 1205 |
+
def decipher_only(self) -> bool:
|
| 1206 |
+
if not self.key_agreement:
|
| 1207 |
+
raise ValueError(
|
| 1208 |
+
"decipher_only is undefined unless key_agreement is true"
|
| 1209 |
+
)
|
| 1210 |
+
else:
|
| 1211 |
+
return self._decipher_only
|
| 1212 |
+
|
| 1213 |
+
def __repr__(self) -> str:
|
| 1214 |
+
try:
|
| 1215 |
+
encipher_only = self.encipher_only
|
| 1216 |
+
decipher_only = self.decipher_only
|
| 1217 |
+
except ValueError:
|
| 1218 |
+
# Users found None confusing because even though encipher/decipher
|
| 1219 |
+
# have no meaning unless key_agreement is true, to construct an
|
| 1220 |
+
# instance of the class you still need to pass False.
|
| 1221 |
+
encipher_only = False
|
| 1222 |
+
decipher_only = False
|
| 1223 |
+
|
| 1224 |
+
return (
|
| 1225 |
+
f"<KeyUsage(digital_signature={self.digital_signature}, "
|
| 1226 |
+
f"content_commitment={self.content_commitment}, "
|
| 1227 |
+
f"key_encipherment={self.key_encipherment}, "
|
| 1228 |
+
f"data_encipherment={self.data_encipherment}, "
|
| 1229 |
+
f"key_agreement={self.key_agreement}, "
|
| 1230 |
+
f"key_cert_sign={self.key_cert_sign}, crl_sign={self.crl_sign}, "
|
| 1231 |
+
f"encipher_only={encipher_only}, decipher_only={decipher_only})>"
|
| 1232 |
+
)
|
| 1233 |
+
|
| 1234 |
+
def __eq__(self, other: object) -> bool:
|
| 1235 |
+
if not isinstance(other, KeyUsage):
|
| 1236 |
+
return NotImplemented
|
| 1237 |
+
|
| 1238 |
+
return (
|
| 1239 |
+
self.digital_signature == other.digital_signature
|
| 1240 |
+
and self.content_commitment == other.content_commitment
|
| 1241 |
+
and self.key_encipherment == other.key_encipherment
|
| 1242 |
+
and self.data_encipherment == other.data_encipherment
|
| 1243 |
+
and self.key_agreement == other.key_agreement
|
| 1244 |
+
and self.key_cert_sign == other.key_cert_sign
|
| 1245 |
+
and self.crl_sign == other.crl_sign
|
| 1246 |
+
and self._encipher_only == other._encipher_only
|
| 1247 |
+
and self._decipher_only == other._decipher_only
|
| 1248 |
+
)
|
| 1249 |
+
|
| 1250 |
+
def __hash__(self) -> int:
|
| 1251 |
+
return hash(
|
| 1252 |
+
(
|
| 1253 |
+
self.digital_signature,
|
| 1254 |
+
self.content_commitment,
|
| 1255 |
+
self.key_encipherment,
|
| 1256 |
+
self.data_encipherment,
|
| 1257 |
+
self.key_agreement,
|
| 1258 |
+
self.key_cert_sign,
|
| 1259 |
+
self.crl_sign,
|
| 1260 |
+
self._encipher_only,
|
| 1261 |
+
self._decipher_only,
|
| 1262 |
+
)
|
| 1263 |
+
)
|
| 1264 |
+
|
| 1265 |
+
def public_bytes(self) -> bytes:
|
| 1266 |
+
return rust_x509.encode_extension_value(self)
|
| 1267 |
+
|
| 1268 |
+
|
| 1269 |
+
class PrivateKeyUsagePeriod(ExtensionType):
|
| 1270 |
+
oid = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD
|
| 1271 |
+
|
| 1272 |
+
def __init__(
|
| 1273 |
+
self,
|
| 1274 |
+
not_before: datetime.datetime | None,
|
| 1275 |
+
not_after: datetime.datetime | None,
|
| 1276 |
+
) -> None:
|
| 1277 |
+
if (
|
| 1278 |
+
not isinstance(not_before, datetime.datetime)
|
| 1279 |
+
and not_before is not None
|
| 1280 |
+
):
|
| 1281 |
+
raise TypeError("not_before must be a datetime.datetime or None")
|
| 1282 |
+
|
| 1283 |
+
if (
|
| 1284 |
+
not isinstance(not_after, datetime.datetime)
|
| 1285 |
+
and not_after is not None
|
| 1286 |
+
):
|
| 1287 |
+
raise TypeError("not_after must be a datetime.datetime or None")
|
| 1288 |
+
|
| 1289 |
+
if not_before is None and not_after is None:
|
| 1290 |
+
raise ValueError(
|
| 1291 |
+
"At least one of not_before and not_after must not be None"
|
| 1292 |
+
)
|
| 1293 |
+
|
| 1294 |
+
if (
|
| 1295 |
+
not_before is not None
|
| 1296 |
+
and not_after is not None
|
| 1297 |
+
and not_before > not_after
|
| 1298 |
+
):
|
| 1299 |
+
raise ValueError("not_before must be before not_after")
|
| 1300 |
+
|
| 1301 |
+
self._not_before = not_before
|
| 1302 |
+
self._not_after = not_after
|
| 1303 |
+
|
| 1304 |
+
@property
|
| 1305 |
+
def not_before(self) -> datetime.datetime | None:
|
| 1306 |
+
return self._not_before
|
| 1307 |
+
|
| 1308 |
+
@property
|
| 1309 |
+
def not_after(self) -> datetime.datetime | None:
|
| 1310 |
+
return self._not_after
|
| 1311 |
+
|
| 1312 |
+
def __repr__(self) -> str:
|
| 1313 |
+
return (
|
| 1314 |
+
f"<PrivateKeyUsagePeriod(not_before={self.not_before}, "
|
| 1315 |
+
f"not_after={self.not_after})>"
|
| 1316 |
+
)
|
| 1317 |
+
|
| 1318 |
+
def __eq__(self, other: object) -> bool:
|
| 1319 |
+
if not isinstance(other, PrivateKeyUsagePeriod):
|
| 1320 |
+
return NotImplemented
|
| 1321 |
+
|
| 1322 |
+
return (
|
| 1323 |
+
self.not_before == other.not_before
|
| 1324 |
+
and self.not_after == other.not_after
|
| 1325 |
+
)
|
| 1326 |
+
|
| 1327 |
+
def __hash__(self) -> int:
|
| 1328 |
+
return hash((self.not_before, self.not_after))
|
| 1329 |
+
|
| 1330 |
+
def public_bytes(self) -> bytes:
|
| 1331 |
+
return rust_x509.encode_extension_value(self)
|
| 1332 |
+
|
| 1333 |
+
|
| 1334 |
+
class NameConstraints(ExtensionType):
|
| 1335 |
+
oid = ExtensionOID.NAME_CONSTRAINTS
|
| 1336 |
+
|
| 1337 |
+
def __init__(
|
| 1338 |
+
self,
|
| 1339 |
+
permitted_subtrees: Iterable[GeneralName] | None,
|
| 1340 |
+
excluded_subtrees: Iterable[GeneralName] | None,
|
| 1341 |
+
) -> None:
|
| 1342 |
+
if permitted_subtrees is not None:
|
| 1343 |
+
permitted_subtrees = list(permitted_subtrees)
|
| 1344 |
+
if not permitted_subtrees:
|
| 1345 |
+
raise ValueError(
|
| 1346 |
+
"permitted_subtrees must be a non-empty list or None"
|
| 1347 |
+
)
|
| 1348 |
+
if not all(isinstance(x, GeneralName) for x in permitted_subtrees):
|
| 1349 |
+
raise TypeError(
|
| 1350 |
+
"permitted_subtrees must be a list of GeneralName objects "
|
| 1351 |
+
"or None"
|
| 1352 |
+
)
|
| 1353 |
+
|
| 1354 |
+
self._validate_tree(permitted_subtrees)
|
| 1355 |
+
|
| 1356 |
+
if excluded_subtrees is not None:
|
| 1357 |
+
excluded_subtrees = list(excluded_subtrees)
|
| 1358 |
+
if not excluded_subtrees:
|
| 1359 |
+
raise ValueError(
|
| 1360 |
+
"excluded_subtrees must be a non-empty list or None"
|
| 1361 |
+
)
|
| 1362 |
+
if not all(isinstance(x, GeneralName) for x in excluded_subtrees):
|
| 1363 |
+
raise TypeError(
|
| 1364 |
+
"excluded_subtrees must be a list of GeneralName objects "
|
| 1365 |
+
"or None"
|
| 1366 |
+
)
|
| 1367 |
+
|
| 1368 |
+
self._validate_tree(excluded_subtrees)
|
| 1369 |
+
|
| 1370 |
+
if permitted_subtrees is None and excluded_subtrees is None:
|
| 1371 |
+
raise ValueError(
|
| 1372 |
+
"At least one of permitted_subtrees and excluded_subtrees "
|
| 1373 |
+
"must not be None"
|
| 1374 |
+
)
|
| 1375 |
+
|
| 1376 |
+
self._permitted_subtrees = permitted_subtrees
|
| 1377 |
+
self._excluded_subtrees = excluded_subtrees
|
| 1378 |
+
|
| 1379 |
+
def __eq__(self, other: object) -> bool:
|
| 1380 |
+
if not isinstance(other, NameConstraints):
|
| 1381 |
+
return NotImplemented
|
| 1382 |
+
|
| 1383 |
+
return (
|
| 1384 |
+
self.excluded_subtrees == other.excluded_subtrees
|
| 1385 |
+
and self.permitted_subtrees == other.permitted_subtrees
|
| 1386 |
+
)
|
| 1387 |
+
|
| 1388 |
+
def _validate_tree(self, tree: Iterable[GeneralName]) -> None:
|
| 1389 |
+
self._validate_ip_name(tree)
|
| 1390 |
+
self._validate_dns_name(tree)
|
| 1391 |
+
|
| 1392 |
+
def _validate_ip_name(self, tree: Iterable[GeneralName]) -> None:
|
| 1393 |
+
if any(
|
| 1394 |
+
isinstance(name, IPAddress)
|
| 1395 |
+
and not isinstance(
|
| 1396 |
+
name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)
|
| 1397 |
+
)
|
| 1398 |
+
for name in tree
|
| 1399 |
+
):
|
| 1400 |
+
raise TypeError(
|
| 1401 |
+
"IPAddress name constraints must be an IPv4Network or"
|
| 1402 |
+
" IPv6Network object"
|
| 1403 |
+
)
|
| 1404 |
+
|
| 1405 |
+
def _validate_dns_name(self, tree: Iterable[GeneralName]) -> None:
|
| 1406 |
+
if any(
|
| 1407 |
+
isinstance(name, DNSName) and "*" in name.value for name in tree
|
| 1408 |
+
):
|
| 1409 |
+
raise ValueError(
|
| 1410 |
+
"DNSName name constraints must not contain the '*' wildcard"
|
| 1411 |
+
" character"
|
| 1412 |
+
)
|
| 1413 |
+
|
| 1414 |
+
def __repr__(self) -> str:
|
| 1415 |
+
return (
|
| 1416 |
+
f"<NameConstraints(permitted_subtrees={self.permitted_subtrees}, "
|
| 1417 |
+
f"excluded_subtrees={self.excluded_subtrees})>"
|
| 1418 |
+
)
|
| 1419 |
+
|
| 1420 |
+
def __hash__(self) -> int:
|
| 1421 |
+
if self.permitted_subtrees is not None:
|
| 1422 |
+
ps: tuple[GeneralName, ...] | None = tuple(self.permitted_subtrees)
|
| 1423 |
+
else:
|
| 1424 |
+
ps = None
|
| 1425 |
+
|
| 1426 |
+
if self.excluded_subtrees is not None:
|
| 1427 |
+
es: tuple[GeneralName, ...] | None = tuple(self.excluded_subtrees)
|
| 1428 |
+
else:
|
| 1429 |
+
es = None
|
| 1430 |
+
|
| 1431 |
+
return hash((ps, es))
|
| 1432 |
+
|
| 1433 |
+
@property
|
| 1434 |
+
def permitted_subtrees(
|
| 1435 |
+
self,
|
| 1436 |
+
) -> list[GeneralName] | None:
|
| 1437 |
+
return self._permitted_subtrees
|
| 1438 |
+
|
| 1439 |
+
@property
|
| 1440 |
+
def excluded_subtrees(
|
| 1441 |
+
self,
|
| 1442 |
+
) -> list[GeneralName] | None:
|
| 1443 |
+
return self._excluded_subtrees
|
| 1444 |
+
|
| 1445 |
+
def public_bytes(self) -> bytes:
|
| 1446 |
+
return rust_x509.encode_extension_value(self)
|
| 1447 |
+
|
| 1448 |
+
|
| 1449 |
+
class Extension(typing.Generic[ExtensionTypeVar]):
|
| 1450 |
+
def __init__(
|
| 1451 |
+
self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar
|
| 1452 |
+
) -> None:
|
| 1453 |
+
if not isinstance(oid, ObjectIdentifier):
|
| 1454 |
+
raise TypeError(
|
| 1455 |
+
"oid argument must be an ObjectIdentifier instance."
|
| 1456 |
+
)
|
| 1457 |
+
|
| 1458 |
+
if not isinstance(critical, bool):
|
| 1459 |
+
raise TypeError("critical must be a boolean value")
|
| 1460 |
+
|
| 1461 |
+
self._oid = oid
|
| 1462 |
+
self._critical = critical
|
| 1463 |
+
self._value = value
|
| 1464 |
+
|
| 1465 |
+
@property
|
| 1466 |
+
def oid(self) -> ObjectIdentifier:
|
| 1467 |
+
return self._oid
|
| 1468 |
+
|
| 1469 |
+
@property
|
| 1470 |
+
def critical(self) -> bool:
|
| 1471 |
+
return self._critical
|
| 1472 |
+
|
| 1473 |
+
@property
|
| 1474 |
+
def value(self) -> ExtensionTypeVar:
|
| 1475 |
+
return self._value
|
| 1476 |
+
|
| 1477 |
+
def __repr__(self) -> str:
|
| 1478 |
+
return (
|
| 1479 |
+
f"<Extension(oid={self.oid}, critical={self.critical}, "
|
| 1480 |
+
f"value={self.value})>"
|
| 1481 |
+
)
|
| 1482 |
+
|
| 1483 |
+
def __eq__(self, other: object) -> bool:
|
| 1484 |
+
if not isinstance(other, Extension):
|
| 1485 |
+
return NotImplemented
|
| 1486 |
+
|
| 1487 |
+
return (
|
| 1488 |
+
self.oid == other.oid
|
| 1489 |
+
and self.critical == other.critical
|
| 1490 |
+
and self.value == other.value
|
| 1491 |
+
)
|
| 1492 |
+
|
| 1493 |
+
def __hash__(self) -> int:
|
| 1494 |
+
return hash((self.oid, self.critical, self.value))
|
| 1495 |
+
|
| 1496 |
+
|
| 1497 |
+
class GeneralNames:
|
| 1498 |
+
def __init__(self, general_names: Iterable[GeneralName]) -> None:
|
| 1499 |
+
general_names = list(general_names)
|
| 1500 |
+
if not all(isinstance(x, GeneralName) for x in general_names):
|
| 1501 |
+
raise TypeError(
|
| 1502 |
+
"Every item in the general_names list must be an "
|
| 1503 |
+
"object conforming to the GeneralName interface"
|
| 1504 |
+
)
|
| 1505 |
+
|
| 1506 |
+
self._general_names = general_names
|
| 1507 |
+
|
| 1508 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
|
| 1509 |
+
|
| 1510 |
+
@typing.overload
|
| 1511 |
+
def get_values_for_type(
|
| 1512 |
+
self,
|
| 1513 |
+
type: type[DNSName]
|
| 1514 |
+
| type[UniformResourceIdentifier]
|
| 1515 |
+
| type[RFC822Name],
|
| 1516 |
+
) -> list[str]: ...
|
| 1517 |
+
|
| 1518 |
+
@typing.overload
|
| 1519 |
+
def get_values_for_type(
|
| 1520 |
+
self,
|
| 1521 |
+
type: type[DirectoryName],
|
| 1522 |
+
) -> list[Name]: ...
|
| 1523 |
+
|
| 1524 |
+
@typing.overload
|
| 1525 |
+
def get_values_for_type(
|
| 1526 |
+
self,
|
| 1527 |
+
type: type[RegisteredID],
|
| 1528 |
+
) -> list[ObjectIdentifier]: ...
|
| 1529 |
+
|
| 1530 |
+
@typing.overload
|
| 1531 |
+
def get_values_for_type(
|
| 1532 |
+
self, type: type[IPAddress]
|
| 1533 |
+
) -> list[_IPAddressTypes]: ...
|
| 1534 |
+
|
| 1535 |
+
@typing.overload
|
| 1536 |
+
def get_values_for_type(
|
| 1537 |
+
self, type: type[OtherName]
|
| 1538 |
+
) -> list[OtherName]: ...
|
| 1539 |
+
|
| 1540 |
+
def get_values_for_type(
|
| 1541 |
+
self,
|
| 1542 |
+
type: type[DNSName]
|
| 1543 |
+
| type[DirectoryName]
|
| 1544 |
+
| type[IPAddress]
|
| 1545 |
+
| type[OtherName]
|
| 1546 |
+
| type[RFC822Name]
|
| 1547 |
+
| type[RegisteredID]
|
| 1548 |
+
| type[UniformResourceIdentifier],
|
| 1549 |
+
) -> (
|
| 1550 |
+
list[_IPAddressTypes]
|
| 1551 |
+
| list[str]
|
| 1552 |
+
| list[OtherName]
|
| 1553 |
+
| list[Name]
|
| 1554 |
+
| list[ObjectIdentifier]
|
| 1555 |
+
):
|
| 1556 |
+
# Return the value of each GeneralName, except for OtherName instances
|
| 1557 |
+
# which we return directly because it has two important properties not
|
| 1558 |
+
# just one value.
|
| 1559 |
+
objs = (i for i in self if isinstance(i, type))
|
| 1560 |
+
if type != OtherName:
|
| 1561 |
+
return [i.value for i in objs] # type: ignore[return-value,unused-ignore]
|
| 1562 |
+
return list(objs) # type: ignore[return-value,unused-ignore]
|
| 1563 |
+
|
| 1564 |
+
def __repr__(self) -> str:
|
| 1565 |
+
return f"<GeneralNames({self._general_names})>"
|
| 1566 |
+
|
| 1567 |
+
def __eq__(self, other: object) -> bool:
|
| 1568 |
+
if not isinstance(other, GeneralNames):
|
| 1569 |
+
return NotImplemented
|
| 1570 |
+
|
| 1571 |
+
return self._general_names == other._general_names
|
| 1572 |
+
|
| 1573 |
+
def __hash__(self) -> int:
|
| 1574 |
+
return hash(tuple(self._general_names))
|
| 1575 |
+
|
| 1576 |
+
|
| 1577 |
+
class SubjectAlternativeName(ExtensionType):
|
| 1578 |
+
oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME
|
| 1579 |
+
|
| 1580 |
+
def __init__(self, general_names: Iterable[GeneralName]) -> None:
|
| 1581 |
+
self._general_names = GeneralNames(general_names)
|
| 1582 |
+
|
| 1583 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
|
| 1584 |
+
|
| 1585 |
+
@typing.overload
|
| 1586 |
+
def get_values_for_type(
|
| 1587 |
+
self,
|
| 1588 |
+
type: type[DNSName]
|
| 1589 |
+
| type[UniformResourceIdentifier]
|
| 1590 |
+
| type[RFC822Name],
|
| 1591 |
+
) -> list[str]: ...
|
| 1592 |
+
|
| 1593 |
+
@typing.overload
|
| 1594 |
+
def get_values_for_type(
|
| 1595 |
+
self,
|
| 1596 |
+
type: type[DirectoryName],
|
| 1597 |
+
) -> list[Name]: ...
|
| 1598 |
+
|
| 1599 |
+
@typing.overload
|
| 1600 |
+
def get_values_for_type(
|
| 1601 |
+
self,
|
| 1602 |
+
type: type[RegisteredID],
|
| 1603 |
+
) -> list[ObjectIdentifier]: ...
|
| 1604 |
+
|
| 1605 |
+
@typing.overload
|
| 1606 |
+
def get_values_for_type(
|
| 1607 |
+
self, type: type[IPAddress]
|
| 1608 |
+
) -> list[_IPAddressTypes]: ...
|
| 1609 |
+
|
| 1610 |
+
@typing.overload
|
| 1611 |
+
def get_values_for_type(
|
| 1612 |
+
self, type: type[OtherName]
|
| 1613 |
+
) -> list[OtherName]: ...
|
| 1614 |
+
|
| 1615 |
+
def get_values_for_type(
|
| 1616 |
+
self,
|
| 1617 |
+
type: type[DNSName]
|
| 1618 |
+
| type[DirectoryName]
|
| 1619 |
+
| type[IPAddress]
|
| 1620 |
+
| type[OtherName]
|
| 1621 |
+
| type[RFC822Name]
|
| 1622 |
+
| type[RegisteredID]
|
| 1623 |
+
| type[UniformResourceIdentifier],
|
| 1624 |
+
) -> (
|
| 1625 |
+
list[_IPAddressTypes]
|
| 1626 |
+
| list[str]
|
| 1627 |
+
| list[OtherName]
|
| 1628 |
+
| list[Name]
|
| 1629 |
+
| list[ObjectIdentifier]
|
| 1630 |
+
):
|
| 1631 |
+
return self._general_names.get_values_for_type(type)
|
| 1632 |
+
|
| 1633 |
+
def __repr__(self) -> str:
|
| 1634 |
+
return f"<SubjectAlternativeName({self._general_names})>"
|
| 1635 |
+
|
| 1636 |
+
def __eq__(self, other: object) -> bool:
|
| 1637 |
+
if not isinstance(other, SubjectAlternativeName):
|
| 1638 |
+
return NotImplemented
|
| 1639 |
+
|
| 1640 |
+
return self._general_names == other._general_names
|
| 1641 |
+
|
| 1642 |
+
def __hash__(self) -> int:
|
| 1643 |
+
return hash(self._general_names)
|
| 1644 |
+
|
| 1645 |
+
def public_bytes(self) -> bytes:
|
| 1646 |
+
return rust_x509.encode_extension_value(self)
|
| 1647 |
+
|
| 1648 |
+
|
| 1649 |
+
class IssuerAlternativeName(ExtensionType):
|
| 1650 |
+
oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME
|
| 1651 |
+
|
| 1652 |
+
def __init__(self, general_names: Iterable[GeneralName]) -> None:
|
| 1653 |
+
self._general_names = GeneralNames(general_names)
|
| 1654 |
+
|
| 1655 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
|
| 1656 |
+
|
| 1657 |
+
@typing.overload
|
| 1658 |
+
def get_values_for_type(
|
| 1659 |
+
self,
|
| 1660 |
+
type: type[DNSName]
|
| 1661 |
+
| type[UniformResourceIdentifier]
|
| 1662 |
+
| type[RFC822Name],
|
| 1663 |
+
) -> list[str]: ...
|
| 1664 |
+
|
| 1665 |
+
@typing.overload
|
| 1666 |
+
def get_values_for_type(
|
| 1667 |
+
self,
|
| 1668 |
+
type: type[DirectoryName],
|
| 1669 |
+
) -> list[Name]: ...
|
| 1670 |
+
|
| 1671 |
+
@typing.overload
|
| 1672 |
+
def get_values_for_type(
|
| 1673 |
+
self,
|
| 1674 |
+
type: type[RegisteredID],
|
| 1675 |
+
) -> list[ObjectIdentifier]: ...
|
| 1676 |
+
|
| 1677 |
+
@typing.overload
|
| 1678 |
+
def get_values_for_type(
|
| 1679 |
+
self, type: type[IPAddress]
|
| 1680 |
+
) -> list[_IPAddressTypes]: ...
|
| 1681 |
+
|
| 1682 |
+
@typing.overload
|
| 1683 |
+
def get_values_for_type(
|
| 1684 |
+
self, type: type[OtherName]
|
| 1685 |
+
) -> list[OtherName]: ...
|
| 1686 |
+
|
| 1687 |
+
def get_values_for_type(
|
| 1688 |
+
self,
|
| 1689 |
+
type: type[DNSName]
|
| 1690 |
+
| type[DirectoryName]
|
| 1691 |
+
| type[IPAddress]
|
| 1692 |
+
| type[OtherName]
|
| 1693 |
+
| type[RFC822Name]
|
| 1694 |
+
| type[RegisteredID]
|
| 1695 |
+
| type[UniformResourceIdentifier],
|
| 1696 |
+
) -> (
|
| 1697 |
+
list[_IPAddressTypes]
|
| 1698 |
+
| list[str]
|
| 1699 |
+
| list[OtherName]
|
| 1700 |
+
| list[Name]
|
| 1701 |
+
| list[ObjectIdentifier]
|
| 1702 |
+
):
|
| 1703 |
+
return self._general_names.get_values_for_type(type)
|
| 1704 |
+
|
| 1705 |
+
def __repr__(self) -> str:
|
| 1706 |
+
return f"<IssuerAlternativeName({self._general_names})>"
|
| 1707 |
+
|
| 1708 |
+
def __eq__(self, other: object) -> bool:
|
| 1709 |
+
if not isinstance(other, IssuerAlternativeName):
|
| 1710 |
+
return NotImplemented
|
| 1711 |
+
|
| 1712 |
+
return self._general_names == other._general_names
|
| 1713 |
+
|
| 1714 |
+
def __hash__(self) -> int:
|
| 1715 |
+
return hash(self._general_names)
|
| 1716 |
+
|
| 1717 |
+
def public_bytes(self) -> bytes:
|
| 1718 |
+
return rust_x509.encode_extension_value(self)
|
| 1719 |
+
|
| 1720 |
+
|
| 1721 |
+
class CertificateIssuer(ExtensionType):
|
| 1722 |
+
oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER
|
| 1723 |
+
|
| 1724 |
+
def __init__(self, general_names: Iterable[GeneralName]) -> None:
|
| 1725 |
+
self._general_names = GeneralNames(general_names)
|
| 1726 |
+
|
| 1727 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
|
| 1728 |
+
|
| 1729 |
+
@typing.overload
|
| 1730 |
+
def get_values_for_type(
|
| 1731 |
+
self,
|
| 1732 |
+
type: type[DNSName]
|
| 1733 |
+
| type[UniformResourceIdentifier]
|
| 1734 |
+
| type[RFC822Name],
|
| 1735 |
+
) -> list[str]: ...
|
| 1736 |
+
|
| 1737 |
+
@typing.overload
|
| 1738 |
+
def get_values_for_type(
|
| 1739 |
+
self,
|
| 1740 |
+
type: type[DirectoryName],
|
| 1741 |
+
) -> list[Name]: ...
|
| 1742 |
+
|
| 1743 |
+
@typing.overload
|
| 1744 |
+
def get_values_for_type(
|
| 1745 |
+
self,
|
| 1746 |
+
type: type[RegisteredID],
|
| 1747 |
+
) -> list[ObjectIdentifier]: ...
|
| 1748 |
+
|
| 1749 |
+
@typing.overload
|
| 1750 |
+
def get_values_for_type(
|
| 1751 |
+
self, type: type[IPAddress]
|
| 1752 |
+
) -> list[_IPAddressTypes]: ...
|
| 1753 |
+
|
| 1754 |
+
@typing.overload
|
| 1755 |
+
def get_values_for_type(
|
| 1756 |
+
self, type: type[OtherName]
|
| 1757 |
+
) -> list[OtherName]: ...
|
| 1758 |
+
|
| 1759 |
+
def get_values_for_type(
|
| 1760 |
+
self,
|
| 1761 |
+
type: type[DNSName]
|
| 1762 |
+
| type[DirectoryName]
|
| 1763 |
+
| type[IPAddress]
|
| 1764 |
+
| type[OtherName]
|
| 1765 |
+
| type[RFC822Name]
|
| 1766 |
+
| type[RegisteredID]
|
| 1767 |
+
| type[UniformResourceIdentifier],
|
| 1768 |
+
) -> (
|
| 1769 |
+
list[_IPAddressTypes]
|
| 1770 |
+
| list[str]
|
| 1771 |
+
| list[OtherName]
|
| 1772 |
+
| list[Name]
|
| 1773 |
+
| list[ObjectIdentifier]
|
| 1774 |
+
):
|
| 1775 |
+
return self._general_names.get_values_for_type(type)
|
| 1776 |
+
|
| 1777 |
+
def __repr__(self) -> str:
|
| 1778 |
+
return f"<CertificateIssuer({self._general_names})>"
|
| 1779 |
+
|
| 1780 |
+
def __eq__(self, other: object) -> bool:
|
| 1781 |
+
if not isinstance(other, CertificateIssuer):
|
| 1782 |
+
return NotImplemented
|
| 1783 |
+
|
| 1784 |
+
return self._general_names == other._general_names
|
| 1785 |
+
|
| 1786 |
+
def __hash__(self) -> int:
|
| 1787 |
+
return hash(self._general_names)
|
| 1788 |
+
|
| 1789 |
+
def public_bytes(self) -> bytes:
|
| 1790 |
+
return rust_x509.encode_extension_value(self)
|
| 1791 |
+
|
| 1792 |
+
|
| 1793 |
+
class CRLReason(ExtensionType):
|
| 1794 |
+
oid = CRLEntryExtensionOID.CRL_REASON
|
| 1795 |
+
|
| 1796 |
+
def __init__(self, reason: ReasonFlags) -> None:
|
| 1797 |
+
if not isinstance(reason, ReasonFlags):
|
| 1798 |
+
raise TypeError("reason must be an element from ReasonFlags")
|
| 1799 |
+
|
| 1800 |
+
self._reason = reason
|
| 1801 |
+
|
| 1802 |
+
def __repr__(self) -> str:
|
| 1803 |
+
return f"<CRLReason(reason={self._reason})>"
|
| 1804 |
+
|
| 1805 |
+
def __eq__(self, other: object) -> bool:
|
| 1806 |
+
if not isinstance(other, CRLReason):
|
| 1807 |
+
return NotImplemented
|
| 1808 |
+
|
| 1809 |
+
return self.reason == other.reason
|
| 1810 |
+
|
| 1811 |
+
def __hash__(self) -> int:
|
| 1812 |
+
return hash(self.reason)
|
| 1813 |
+
|
| 1814 |
+
@property
|
| 1815 |
+
def reason(self) -> ReasonFlags:
|
| 1816 |
+
return self._reason
|
| 1817 |
+
|
| 1818 |
+
def public_bytes(self) -> bytes:
|
| 1819 |
+
return rust_x509.encode_extension_value(self)
|
| 1820 |
+
|
| 1821 |
+
|
| 1822 |
+
class InvalidityDate(ExtensionType):
|
| 1823 |
+
oid = CRLEntryExtensionOID.INVALIDITY_DATE
|
| 1824 |
+
|
| 1825 |
+
def __init__(self, invalidity_date: datetime.datetime) -> None:
|
| 1826 |
+
if not isinstance(invalidity_date, datetime.datetime):
|
| 1827 |
+
raise TypeError("invalidity_date must be a datetime.datetime")
|
| 1828 |
+
|
| 1829 |
+
self._invalidity_date = invalidity_date
|
| 1830 |
+
|
| 1831 |
+
def __repr__(self) -> str:
|
| 1832 |
+
return f"<InvalidityDate(invalidity_date={self._invalidity_date})>"
|
| 1833 |
+
|
| 1834 |
+
def __eq__(self, other: object) -> bool:
|
| 1835 |
+
if not isinstance(other, InvalidityDate):
|
| 1836 |
+
return NotImplemented
|
| 1837 |
+
|
| 1838 |
+
return self.invalidity_date == other.invalidity_date
|
| 1839 |
+
|
| 1840 |
+
def __hash__(self) -> int:
|
| 1841 |
+
return hash(self.invalidity_date)
|
| 1842 |
+
|
| 1843 |
+
@property
|
| 1844 |
+
def invalidity_date(self) -> datetime.datetime:
|
| 1845 |
+
return self._invalidity_date
|
| 1846 |
+
|
| 1847 |
+
@property
|
| 1848 |
+
def invalidity_date_utc(self) -> datetime.datetime:
|
| 1849 |
+
if self._invalidity_date.tzinfo is None:
|
| 1850 |
+
return self._invalidity_date.replace(tzinfo=datetime.timezone.utc)
|
| 1851 |
+
else:
|
| 1852 |
+
return self._invalidity_date.astimezone(tz=datetime.timezone.utc)
|
| 1853 |
+
|
| 1854 |
+
def public_bytes(self) -> bytes:
|
| 1855 |
+
return rust_x509.encode_extension_value(self)
|
| 1856 |
+
|
| 1857 |
+
|
| 1858 |
+
class PrecertificateSignedCertificateTimestamps(ExtensionType):
|
| 1859 |
+
oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS
|
| 1860 |
+
|
| 1861 |
+
def __init__(
|
| 1862 |
+
self,
|
| 1863 |
+
signed_certificate_timestamps: Iterable[SignedCertificateTimestamp],
|
| 1864 |
+
) -> None:
|
| 1865 |
+
signed_certificate_timestamps = list(signed_certificate_timestamps)
|
| 1866 |
+
if not all(
|
| 1867 |
+
isinstance(sct, SignedCertificateTimestamp)
|
| 1868 |
+
for sct in signed_certificate_timestamps
|
| 1869 |
+
):
|
| 1870 |
+
raise TypeError(
|
| 1871 |
+
"Every item in the signed_certificate_timestamps list must be "
|
| 1872 |
+
"a SignedCertificateTimestamp"
|
| 1873 |
+
)
|
| 1874 |
+
self._signed_certificate_timestamps = signed_certificate_timestamps
|
| 1875 |
+
|
| 1876 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods(
|
| 1877 |
+
"_signed_certificate_timestamps"
|
| 1878 |
+
)
|
| 1879 |
+
|
| 1880 |
+
def __repr__(self) -> str:
|
| 1881 |
+
return f"<PrecertificateSignedCertificateTimestamps({list(self)})>"
|
| 1882 |
+
|
| 1883 |
+
def __hash__(self) -> int:
|
| 1884 |
+
return hash(tuple(self._signed_certificate_timestamps))
|
| 1885 |
+
|
| 1886 |
+
def __eq__(self, other: object) -> bool:
|
| 1887 |
+
if not isinstance(other, PrecertificateSignedCertificateTimestamps):
|
| 1888 |
+
return NotImplemented
|
| 1889 |
+
|
| 1890 |
+
return (
|
| 1891 |
+
self._signed_certificate_timestamps
|
| 1892 |
+
== other._signed_certificate_timestamps
|
| 1893 |
+
)
|
| 1894 |
+
|
| 1895 |
+
def public_bytes(self) -> bytes:
|
| 1896 |
+
return rust_x509.encode_extension_value(self)
|
| 1897 |
+
|
| 1898 |
+
|
| 1899 |
+
class SignedCertificateTimestamps(ExtensionType):
|
| 1900 |
+
oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS
|
| 1901 |
+
|
| 1902 |
+
def __init__(
|
| 1903 |
+
self,
|
| 1904 |
+
signed_certificate_timestamps: Iterable[SignedCertificateTimestamp],
|
| 1905 |
+
) -> None:
|
| 1906 |
+
signed_certificate_timestamps = list(signed_certificate_timestamps)
|
| 1907 |
+
if not all(
|
| 1908 |
+
isinstance(sct, SignedCertificateTimestamp)
|
| 1909 |
+
for sct in signed_certificate_timestamps
|
| 1910 |
+
):
|
| 1911 |
+
raise TypeError(
|
| 1912 |
+
"Every item in the signed_certificate_timestamps list must be "
|
| 1913 |
+
"a SignedCertificateTimestamp"
|
| 1914 |
+
)
|
| 1915 |
+
self._signed_certificate_timestamps = signed_certificate_timestamps
|
| 1916 |
+
|
| 1917 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods(
|
| 1918 |
+
"_signed_certificate_timestamps"
|
| 1919 |
+
)
|
| 1920 |
+
|
| 1921 |
+
def __repr__(self) -> str:
|
| 1922 |
+
return f"<SignedCertificateTimestamps({list(self)})>"
|
| 1923 |
+
|
| 1924 |
+
def __hash__(self) -> int:
|
| 1925 |
+
return hash(tuple(self._signed_certificate_timestamps))
|
| 1926 |
+
|
| 1927 |
+
def __eq__(self, other: object) -> bool:
|
| 1928 |
+
if not isinstance(other, SignedCertificateTimestamps):
|
| 1929 |
+
return NotImplemented
|
| 1930 |
+
|
| 1931 |
+
return (
|
| 1932 |
+
self._signed_certificate_timestamps
|
| 1933 |
+
== other._signed_certificate_timestamps
|
| 1934 |
+
)
|
| 1935 |
+
|
| 1936 |
+
def public_bytes(self) -> bytes:
|
| 1937 |
+
return rust_x509.encode_extension_value(self)
|
| 1938 |
+
|
| 1939 |
+
|
| 1940 |
+
class OCSPNonce(ExtensionType):
|
| 1941 |
+
oid = OCSPExtensionOID.NONCE
|
| 1942 |
+
|
| 1943 |
+
def __init__(self, nonce: bytes) -> None:
|
| 1944 |
+
if not isinstance(nonce, bytes):
|
| 1945 |
+
raise TypeError("nonce must be bytes")
|
| 1946 |
+
|
| 1947 |
+
self._nonce = nonce
|
| 1948 |
+
|
| 1949 |
+
def __eq__(self, other: object) -> bool:
|
| 1950 |
+
if not isinstance(other, OCSPNonce):
|
| 1951 |
+
return NotImplemented
|
| 1952 |
+
|
| 1953 |
+
return self.nonce == other.nonce
|
| 1954 |
+
|
| 1955 |
+
def __hash__(self) -> int:
|
| 1956 |
+
return hash(self.nonce)
|
| 1957 |
+
|
| 1958 |
+
def __repr__(self) -> str:
|
| 1959 |
+
return f"<OCSPNonce(nonce={self.nonce!r})>"
|
| 1960 |
+
|
| 1961 |
+
@property
|
| 1962 |
+
def nonce(self) -> bytes:
|
| 1963 |
+
return self._nonce
|
| 1964 |
+
|
| 1965 |
+
def public_bytes(self) -> bytes:
|
| 1966 |
+
return rust_x509.encode_extension_value(self)
|
| 1967 |
+
|
| 1968 |
+
|
| 1969 |
+
class OCSPAcceptableResponses(ExtensionType):
|
| 1970 |
+
oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES
|
| 1971 |
+
|
| 1972 |
+
def __init__(self, responses: Iterable[ObjectIdentifier]) -> None:
|
| 1973 |
+
responses = list(responses)
|
| 1974 |
+
if any(not isinstance(r, ObjectIdentifier) for r in responses):
|
| 1975 |
+
raise TypeError("All responses must be ObjectIdentifiers")
|
| 1976 |
+
|
| 1977 |
+
self._responses = responses
|
| 1978 |
+
|
| 1979 |
+
def __eq__(self, other: object) -> bool:
|
| 1980 |
+
if not isinstance(other, OCSPAcceptableResponses):
|
| 1981 |
+
return NotImplemented
|
| 1982 |
+
|
| 1983 |
+
return self._responses == other._responses
|
| 1984 |
+
|
| 1985 |
+
def __hash__(self) -> int:
|
| 1986 |
+
return hash(tuple(self._responses))
|
| 1987 |
+
|
| 1988 |
+
def __repr__(self) -> str:
|
| 1989 |
+
return f"<OCSPAcceptableResponses(responses={self._responses})>"
|
| 1990 |
+
|
| 1991 |
+
def __iter__(self) -> Iterator[ObjectIdentifier]:
|
| 1992 |
+
return iter(self._responses)
|
| 1993 |
+
|
| 1994 |
+
def public_bytes(self) -> bytes:
|
| 1995 |
+
return rust_x509.encode_extension_value(self)
|
| 1996 |
+
|
| 1997 |
+
|
| 1998 |
+
class IssuingDistributionPoint(ExtensionType):
|
| 1999 |
+
oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT
|
| 2000 |
+
|
| 2001 |
+
def __init__(
|
| 2002 |
+
self,
|
| 2003 |
+
full_name: Iterable[GeneralName] | None,
|
| 2004 |
+
relative_name: RelativeDistinguishedName | None,
|
| 2005 |
+
only_contains_user_certs: bool,
|
| 2006 |
+
only_contains_ca_certs: bool,
|
| 2007 |
+
only_some_reasons: frozenset[ReasonFlags] | None,
|
| 2008 |
+
indirect_crl: bool,
|
| 2009 |
+
only_contains_attribute_certs: bool,
|
| 2010 |
+
) -> None:
|
| 2011 |
+
if full_name is not None:
|
| 2012 |
+
full_name = list(full_name)
|
| 2013 |
+
|
| 2014 |
+
if only_some_reasons and (
|
| 2015 |
+
not isinstance(only_some_reasons, frozenset)
|
| 2016 |
+
or not all(isinstance(x, ReasonFlags) for x in only_some_reasons)
|
| 2017 |
+
):
|
| 2018 |
+
raise TypeError(
|
| 2019 |
+
"only_some_reasons must be None or frozenset of ReasonFlags"
|
| 2020 |
+
)
|
| 2021 |
+
|
| 2022 |
+
if only_some_reasons and (
|
| 2023 |
+
ReasonFlags.unspecified in only_some_reasons
|
| 2024 |
+
or ReasonFlags.remove_from_crl in only_some_reasons
|
| 2025 |
+
):
|
| 2026 |
+
raise ValueError(
|
| 2027 |
+
"unspecified and remove_from_crl are not valid reasons in an "
|
| 2028 |
+
"IssuingDistributionPoint"
|
| 2029 |
+
)
|
| 2030 |
+
|
| 2031 |
+
if not (
|
| 2032 |
+
isinstance(only_contains_user_certs, bool)
|
| 2033 |
+
and isinstance(only_contains_ca_certs, bool)
|
| 2034 |
+
and isinstance(indirect_crl, bool)
|
| 2035 |
+
and isinstance(only_contains_attribute_certs, bool)
|
| 2036 |
+
):
|
| 2037 |
+
raise TypeError(
|
| 2038 |
+
"only_contains_user_certs, only_contains_ca_certs, "
|
| 2039 |
+
"indirect_crl and only_contains_attribute_certs "
|
| 2040 |
+
"must all be boolean."
|
| 2041 |
+
)
|
| 2042 |
+
|
| 2043 |
+
# Per RFC5280 Section 5.2.5, the Issuing Distribution Point extension
|
| 2044 |
+
# in a CRL can have only one of onlyContainsUserCerts,
|
| 2045 |
+
# onlyContainsCACerts, onlyContainsAttributeCerts set to TRUE.
|
| 2046 |
+
crl_constraints = [
|
| 2047 |
+
only_contains_user_certs,
|
| 2048 |
+
only_contains_ca_certs,
|
| 2049 |
+
only_contains_attribute_certs,
|
| 2050 |
+
]
|
| 2051 |
+
|
| 2052 |
+
if len([x for x in crl_constraints if x]) > 1:
|
| 2053 |
+
raise ValueError(
|
| 2054 |
+
"Only one of the following can be set to True: "
|
| 2055 |
+
"only_contains_user_certs, only_contains_ca_certs, "
|
| 2056 |
+
"only_contains_attribute_certs"
|
| 2057 |
+
)
|
| 2058 |
+
|
| 2059 |
+
if not any(
|
| 2060 |
+
[
|
| 2061 |
+
only_contains_user_certs,
|
| 2062 |
+
only_contains_ca_certs,
|
| 2063 |
+
indirect_crl,
|
| 2064 |
+
only_contains_attribute_certs,
|
| 2065 |
+
full_name,
|
| 2066 |
+
relative_name,
|
| 2067 |
+
only_some_reasons,
|
| 2068 |
+
]
|
| 2069 |
+
):
|
| 2070 |
+
raise ValueError(
|
| 2071 |
+
"Cannot create empty extension: "
|
| 2072 |
+
"if only_contains_user_certs, only_contains_ca_certs, "
|
| 2073 |
+
"indirect_crl, and only_contains_attribute_certs are all False"
|
| 2074 |
+
", then either full_name, relative_name, or only_some_reasons "
|
| 2075 |
+
"must have a value."
|
| 2076 |
+
)
|
| 2077 |
+
|
| 2078 |
+
self._only_contains_user_certs = only_contains_user_certs
|
| 2079 |
+
self._only_contains_ca_certs = only_contains_ca_certs
|
| 2080 |
+
self._indirect_crl = indirect_crl
|
| 2081 |
+
self._only_contains_attribute_certs = only_contains_attribute_certs
|
| 2082 |
+
self._only_some_reasons = only_some_reasons
|
| 2083 |
+
self._full_name = full_name
|
| 2084 |
+
self._relative_name = relative_name
|
| 2085 |
+
|
| 2086 |
+
def __repr__(self) -> str:
|
| 2087 |
+
return (
|
| 2088 |
+
f"<IssuingDistributionPoint(full_name={self.full_name}, "
|
| 2089 |
+
f"relative_name={self.relative_name}, "
|
| 2090 |
+
f"only_contains_user_certs={self.only_contains_user_certs}, "
|
| 2091 |
+
f"only_contains_ca_certs={self.only_contains_ca_certs}, "
|
| 2092 |
+
f"only_some_reasons={self.only_some_reasons}, "
|
| 2093 |
+
f"indirect_crl={self.indirect_crl}, "
|
| 2094 |
+
"only_contains_attribute_certs="
|
| 2095 |
+
f"{self.only_contains_attribute_certs})>"
|
| 2096 |
+
)
|
| 2097 |
+
|
| 2098 |
+
def __eq__(self, other: object) -> bool:
|
| 2099 |
+
if not isinstance(other, IssuingDistributionPoint):
|
| 2100 |
+
return NotImplemented
|
| 2101 |
+
|
| 2102 |
+
return (
|
| 2103 |
+
self.full_name == other.full_name
|
| 2104 |
+
and self.relative_name == other.relative_name
|
| 2105 |
+
and self.only_contains_user_certs == other.only_contains_user_certs
|
| 2106 |
+
and self.only_contains_ca_certs == other.only_contains_ca_certs
|
| 2107 |
+
and self.only_some_reasons == other.only_some_reasons
|
| 2108 |
+
and self.indirect_crl == other.indirect_crl
|
| 2109 |
+
and self.only_contains_attribute_certs
|
| 2110 |
+
== other.only_contains_attribute_certs
|
| 2111 |
+
)
|
| 2112 |
+
|
| 2113 |
+
def __hash__(self) -> int:
|
| 2114 |
+
if self.full_name is not None:
|
| 2115 |
+
full_name: tuple[GeneralName, ...] | None = tuple(self.full_name)
|
| 2116 |
+
else:
|
| 2117 |
+
full_name = None
|
| 2118 |
+
|
| 2119 |
+
return hash(
|
| 2120 |
+
(
|
| 2121 |
+
full_name,
|
| 2122 |
+
self.relative_name,
|
| 2123 |
+
self.only_contains_user_certs,
|
| 2124 |
+
self.only_contains_ca_certs,
|
| 2125 |
+
self.only_some_reasons,
|
| 2126 |
+
self.indirect_crl,
|
| 2127 |
+
self.only_contains_attribute_certs,
|
| 2128 |
+
)
|
| 2129 |
+
)
|
| 2130 |
+
|
| 2131 |
+
@property
|
| 2132 |
+
def full_name(self) -> list[GeneralName] | None:
|
| 2133 |
+
return self._full_name
|
| 2134 |
+
|
| 2135 |
+
@property
|
| 2136 |
+
def relative_name(self) -> RelativeDistinguishedName | None:
|
| 2137 |
+
return self._relative_name
|
| 2138 |
+
|
| 2139 |
+
@property
|
| 2140 |
+
def only_contains_user_certs(self) -> bool:
|
| 2141 |
+
return self._only_contains_user_certs
|
| 2142 |
+
|
| 2143 |
+
@property
|
| 2144 |
+
def only_contains_ca_certs(self) -> bool:
|
| 2145 |
+
return self._only_contains_ca_certs
|
| 2146 |
+
|
| 2147 |
+
@property
|
| 2148 |
+
def only_some_reasons(
|
| 2149 |
+
self,
|
| 2150 |
+
) -> frozenset[ReasonFlags] | None:
|
| 2151 |
+
return self._only_some_reasons
|
| 2152 |
+
|
| 2153 |
+
@property
|
| 2154 |
+
def indirect_crl(self) -> bool:
|
| 2155 |
+
return self._indirect_crl
|
| 2156 |
+
|
| 2157 |
+
@property
|
| 2158 |
+
def only_contains_attribute_certs(self) -> bool:
|
| 2159 |
+
return self._only_contains_attribute_certs
|
| 2160 |
+
|
| 2161 |
+
def public_bytes(self) -> bytes:
|
| 2162 |
+
return rust_x509.encode_extension_value(self)
|
| 2163 |
+
|
| 2164 |
+
|
| 2165 |
+
class MSCertificateTemplate(ExtensionType):
|
| 2166 |
+
oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE
|
| 2167 |
+
|
| 2168 |
+
def __init__(
|
| 2169 |
+
self,
|
| 2170 |
+
template_id: ObjectIdentifier,
|
| 2171 |
+
major_version: int | None,
|
| 2172 |
+
minor_version: int | None,
|
| 2173 |
+
) -> None:
|
| 2174 |
+
if not isinstance(template_id, ObjectIdentifier):
|
| 2175 |
+
raise TypeError("oid must be an ObjectIdentifier")
|
| 2176 |
+
self._template_id = template_id
|
| 2177 |
+
if (
|
| 2178 |
+
major_version is not None and not isinstance(major_version, int)
|
| 2179 |
+
) or (
|
| 2180 |
+
minor_version is not None and not isinstance(minor_version, int)
|
| 2181 |
+
):
|
| 2182 |
+
raise TypeError(
|
| 2183 |
+
"major_version and minor_version must be integers or None"
|
| 2184 |
+
)
|
| 2185 |
+
self._major_version = major_version
|
| 2186 |
+
self._minor_version = minor_version
|
| 2187 |
+
|
| 2188 |
+
@property
|
| 2189 |
+
def template_id(self) -> ObjectIdentifier:
|
| 2190 |
+
return self._template_id
|
| 2191 |
+
|
| 2192 |
+
@property
|
| 2193 |
+
def major_version(self) -> int | None:
|
| 2194 |
+
return self._major_version
|
| 2195 |
+
|
| 2196 |
+
@property
|
| 2197 |
+
def minor_version(self) -> int | None:
|
| 2198 |
+
return self._minor_version
|
| 2199 |
+
|
| 2200 |
+
def __repr__(self) -> str:
|
| 2201 |
+
return (
|
| 2202 |
+
f"<MSCertificateTemplate(template_id={self.template_id}, "
|
| 2203 |
+
f"major_version={self.major_version}, "
|
| 2204 |
+
f"minor_version={self.minor_version})>"
|
| 2205 |
+
)
|
| 2206 |
+
|
| 2207 |
+
def __eq__(self, other: object) -> bool:
|
| 2208 |
+
if not isinstance(other, MSCertificateTemplate):
|
| 2209 |
+
return NotImplemented
|
| 2210 |
+
|
| 2211 |
+
return (
|
| 2212 |
+
self.template_id == other.template_id
|
| 2213 |
+
and self.major_version == other.major_version
|
| 2214 |
+
and self.minor_version == other.minor_version
|
| 2215 |
+
)
|
| 2216 |
+
|
| 2217 |
+
def __hash__(self) -> int:
|
| 2218 |
+
return hash((self.template_id, self.major_version, self.minor_version))
|
| 2219 |
+
|
| 2220 |
+
def public_bytes(self) -> bytes:
|
| 2221 |
+
return rust_x509.encode_extension_value(self)
|
| 2222 |
+
|
| 2223 |
+
|
| 2224 |
+
class NamingAuthority:
|
| 2225 |
+
def __init__(
|
| 2226 |
+
self,
|
| 2227 |
+
id: ObjectIdentifier | None,
|
| 2228 |
+
url: str | None,
|
| 2229 |
+
text: str | None,
|
| 2230 |
+
) -> None:
|
| 2231 |
+
if id is not None and not isinstance(id, ObjectIdentifier):
|
| 2232 |
+
raise TypeError("id must be an ObjectIdentifier")
|
| 2233 |
+
|
| 2234 |
+
if url is not None and not isinstance(url, str):
|
| 2235 |
+
raise TypeError("url must be a str")
|
| 2236 |
+
|
| 2237 |
+
if text is not None and not isinstance(text, str):
|
| 2238 |
+
raise TypeError("text must be a str")
|
| 2239 |
+
|
| 2240 |
+
self._id = id
|
| 2241 |
+
self._url = url
|
| 2242 |
+
self._text = text
|
| 2243 |
+
|
| 2244 |
+
@property
|
| 2245 |
+
def id(self) -> ObjectIdentifier | None:
|
| 2246 |
+
return self._id
|
| 2247 |
+
|
| 2248 |
+
@property
|
| 2249 |
+
def url(self) -> str | None:
|
| 2250 |
+
return self._url
|
| 2251 |
+
|
| 2252 |
+
@property
|
| 2253 |
+
def text(self) -> str | None:
|
| 2254 |
+
return self._text
|
| 2255 |
+
|
| 2256 |
+
def __repr__(self) -> str:
|
| 2257 |
+
return (
|
| 2258 |
+
f"<NamingAuthority("
|
| 2259 |
+
f"id={self.id}, url={self.url}, text={self.text})>"
|
| 2260 |
+
)
|
| 2261 |
+
|
| 2262 |
+
def __eq__(self, other: object) -> bool:
|
| 2263 |
+
if not isinstance(other, NamingAuthority):
|
| 2264 |
+
return NotImplemented
|
| 2265 |
+
|
| 2266 |
+
return (
|
| 2267 |
+
self.id == other.id
|
| 2268 |
+
and self.url == other.url
|
| 2269 |
+
and self.text == other.text
|
| 2270 |
+
)
|
| 2271 |
+
|
| 2272 |
+
def __hash__(self) -> int:
|
| 2273 |
+
return hash(
|
| 2274 |
+
(
|
| 2275 |
+
self.id,
|
| 2276 |
+
self.url,
|
| 2277 |
+
self.text,
|
| 2278 |
+
)
|
| 2279 |
+
)
|
| 2280 |
+
|
| 2281 |
+
|
| 2282 |
+
class ProfessionInfo:
|
| 2283 |
+
def __init__(
|
| 2284 |
+
self,
|
| 2285 |
+
naming_authority: NamingAuthority | None,
|
| 2286 |
+
profession_items: Iterable[str],
|
| 2287 |
+
profession_oids: Iterable[ObjectIdentifier] | None,
|
| 2288 |
+
registration_number: str | None,
|
| 2289 |
+
add_profession_info: bytes | None,
|
| 2290 |
+
) -> None:
|
| 2291 |
+
if naming_authority is not None and not isinstance(
|
| 2292 |
+
naming_authority, NamingAuthority
|
| 2293 |
+
):
|
| 2294 |
+
raise TypeError("naming_authority must be a NamingAuthority")
|
| 2295 |
+
|
| 2296 |
+
profession_items = list(profession_items)
|
| 2297 |
+
if not all(isinstance(item, str) for item in profession_items):
|
| 2298 |
+
raise TypeError(
|
| 2299 |
+
"Every item in the profession_items list must be a str"
|
| 2300 |
+
)
|
| 2301 |
+
|
| 2302 |
+
if profession_oids is not None:
|
| 2303 |
+
profession_oids = list(profession_oids)
|
| 2304 |
+
if not all(
|
| 2305 |
+
isinstance(oid, ObjectIdentifier) for oid in profession_oids
|
| 2306 |
+
):
|
| 2307 |
+
raise TypeError(
|
| 2308 |
+
"Every item in the profession_oids list must be an "
|
| 2309 |
+
"ObjectIdentifier"
|
| 2310 |
+
)
|
| 2311 |
+
|
| 2312 |
+
if registration_number is not None and not isinstance(
|
| 2313 |
+
registration_number, str
|
| 2314 |
+
):
|
| 2315 |
+
raise TypeError("registration_number must be a str")
|
| 2316 |
+
|
| 2317 |
+
if add_profession_info is not None and not isinstance(
|
| 2318 |
+
add_profession_info, bytes
|
| 2319 |
+
):
|
| 2320 |
+
raise TypeError("add_profession_info must be bytes")
|
| 2321 |
+
|
| 2322 |
+
self._naming_authority = naming_authority
|
| 2323 |
+
self._profession_items = profession_items
|
| 2324 |
+
self._profession_oids = profession_oids
|
| 2325 |
+
self._registration_number = registration_number
|
| 2326 |
+
self._add_profession_info = add_profession_info
|
| 2327 |
+
|
| 2328 |
+
@property
|
| 2329 |
+
def naming_authority(self) -> NamingAuthority | None:
|
| 2330 |
+
return self._naming_authority
|
| 2331 |
+
|
| 2332 |
+
@property
|
| 2333 |
+
def profession_items(self) -> list[str]:
|
| 2334 |
+
return self._profession_items
|
| 2335 |
+
|
| 2336 |
+
@property
|
| 2337 |
+
def profession_oids(self) -> list[ObjectIdentifier] | None:
|
| 2338 |
+
return self._profession_oids
|
| 2339 |
+
|
| 2340 |
+
@property
|
| 2341 |
+
def registration_number(self) -> str | None:
|
| 2342 |
+
return self._registration_number
|
| 2343 |
+
|
| 2344 |
+
@property
|
| 2345 |
+
def add_profession_info(self) -> bytes | None:
|
| 2346 |
+
return self._add_profession_info
|
| 2347 |
+
|
| 2348 |
+
def __repr__(self) -> str:
|
| 2349 |
+
return (
|
| 2350 |
+
f"<ProfessionInfo(naming_authority={self.naming_authority}, "
|
| 2351 |
+
f"profession_items={self.profession_items}, "
|
| 2352 |
+
f"profession_oids={self.profession_oids}, "
|
| 2353 |
+
f"registration_number={self.registration_number}, "
|
| 2354 |
+
f"add_profession_info={self.add_profession_info!r})>"
|
| 2355 |
+
)
|
| 2356 |
+
|
| 2357 |
+
def __eq__(self, other: object) -> bool:
|
| 2358 |
+
if not isinstance(other, ProfessionInfo):
|
| 2359 |
+
return NotImplemented
|
| 2360 |
+
|
| 2361 |
+
return (
|
| 2362 |
+
self.naming_authority == other.naming_authority
|
| 2363 |
+
and self.profession_items == other.profession_items
|
| 2364 |
+
and self.profession_oids == other.profession_oids
|
| 2365 |
+
and self.registration_number == other.registration_number
|
| 2366 |
+
and self.add_profession_info == other.add_profession_info
|
| 2367 |
+
)
|
| 2368 |
+
|
| 2369 |
+
def __hash__(self) -> int:
|
| 2370 |
+
if self.profession_oids is not None:
|
| 2371 |
+
profession_oids = tuple(self.profession_oids)
|
| 2372 |
+
else:
|
| 2373 |
+
profession_oids = None
|
| 2374 |
+
return hash(
|
| 2375 |
+
(
|
| 2376 |
+
self.naming_authority,
|
| 2377 |
+
tuple(self.profession_items),
|
| 2378 |
+
profession_oids,
|
| 2379 |
+
self.registration_number,
|
| 2380 |
+
self.add_profession_info,
|
| 2381 |
+
)
|
| 2382 |
+
)
|
| 2383 |
+
|
| 2384 |
+
|
| 2385 |
+
class Admission:
|
| 2386 |
+
def __init__(
|
| 2387 |
+
self,
|
| 2388 |
+
admission_authority: GeneralName | None,
|
| 2389 |
+
naming_authority: NamingAuthority | None,
|
| 2390 |
+
profession_infos: Iterable[ProfessionInfo],
|
| 2391 |
+
) -> None:
|
| 2392 |
+
if admission_authority is not None and not isinstance(
|
| 2393 |
+
admission_authority, GeneralName
|
| 2394 |
+
):
|
| 2395 |
+
raise TypeError("admission_authority must be a GeneralName")
|
| 2396 |
+
|
| 2397 |
+
if naming_authority is not None and not isinstance(
|
| 2398 |
+
naming_authority, NamingAuthority
|
| 2399 |
+
):
|
| 2400 |
+
raise TypeError("naming_authority must be a NamingAuthority")
|
| 2401 |
+
|
| 2402 |
+
profession_infos = list(profession_infos)
|
| 2403 |
+
if not all(
|
| 2404 |
+
isinstance(info, ProfessionInfo) for info in profession_infos
|
| 2405 |
+
):
|
| 2406 |
+
raise TypeError(
|
| 2407 |
+
"Every item in the profession_infos list must be a "
|
| 2408 |
+
"ProfessionInfo"
|
| 2409 |
+
)
|
| 2410 |
+
|
| 2411 |
+
self._admission_authority = admission_authority
|
| 2412 |
+
self._naming_authority = naming_authority
|
| 2413 |
+
self._profession_infos = profession_infos
|
| 2414 |
+
|
| 2415 |
+
@property
|
| 2416 |
+
def admission_authority(self) -> GeneralName | None:
|
| 2417 |
+
return self._admission_authority
|
| 2418 |
+
|
| 2419 |
+
@property
|
| 2420 |
+
def naming_authority(self) -> NamingAuthority | None:
|
| 2421 |
+
return self._naming_authority
|
| 2422 |
+
|
| 2423 |
+
@property
|
| 2424 |
+
def profession_infos(self) -> list[ProfessionInfo]:
|
| 2425 |
+
return self._profession_infos
|
| 2426 |
+
|
| 2427 |
+
def __repr__(self) -> str:
|
| 2428 |
+
return (
|
| 2429 |
+
f"<Admission(admission_authority={self.admission_authority}, "
|
| 2430 |
+
f"naming_authority={self.naming_authority}, "
|
| 2431 |
+
f"profession_infos={self.profession_infos})>"
|
| 2432 |
+
)
|
| 2433 |
+
|
| 2434 |
+
def __eq__(self, other: object) -> bool:
|
| 2435 |
+
if not isinstance(other, Admission):
|
| 2436 |
+
return NotImplemented
|
| 2437 |
+
|
| 2438 |
+
return (
|
| 2439 |
+
self.admission_authority == other.admission_authority
|
| 2440 |
+
and self.naming_authority == other.naming_authority
|
| 2441 |
+
and self.profession_infos == other.profession_infos
|
| 2442 |
+
)
|
| 2443 |
+
|
| 2444 |
+
def __hash__(self) -> int:
|
| 2445 |
+
return hash(
|
| 2446 |
+
(
|
| 2447 |
+
self.admission_authority,
|
| 2448 |
+
self.naming_authority,
|
| 2449 |
+
tuple(self.profession_infos),
|
| 2450 |
+
)
|
| 2451 |
+
)
|
| 2452 |
+
|
| 2453 |
+
|
| 2454 |
+
class Admissions(ExtensionType):
|
| 2455 |
+
oid = ExtensionOID.ADMISSIONS
|
| 2456 |
+
|
| 2457 |
+
def __init__(
|
| 2458 |
+
self,
|
| 2459 |
+
authority: GeneralName | None,
|
| 2460 |
+
admissions: Iterable[Admission],
|
| 2461 |
+
) -> None:
|
| 2462 |
+
if authority is not None and not isinstance(authority, GeneralName):
|
| 2463 |
+
raise TypeError("authority must be a GeneralName")
|
| 2464 |
+
|
| 2465 |
+
admissions = list(admissions)
|
| 2466 |
+
if not all(
|
| 2467 |
+
isinstance(admission, Admission) for admission in admissions
|
| 2468 |
+
):
|
| 2469 |
+
raise TypeError(
|
| 2470 |
+
"Every item in the contents_of_admissions list must be an "
|
| 2471 |
+
"Admission"
|
| 2472 |
+
)
|
| 2473 |
+
|
| 2474 |
+
self._authority = authority
|
| 2475 |
+
self._admissions = admissions
|
| 2476 |
+
|
| 2477 |
+
__len__, __iter__, __getitem__ = _make_sequence_methods("_admissions")
|
| 2478 |
+
|
| 2479 |
+
@property
|
| 2480 |
+
def authority(self) -> GeneralName | None:
|
| 2481 |
+
return self._authority
|
| 2482 |
+
|
| 2483 |
+
def __repr__(self) -> str:
|
| 2484 |
+
return (
|
| 2485 |
+
f"<Admissions(authority={self._authority}, "
|
| 2486 |
+
f"admissions={self._admissions})>"
|
| 2487 |
+
)
|
| 2488 |
+
|
| 2489 |
+
def __eq__(self, other: object) -> bool:
|
| 2490 |
+
if not isinstance(other, Admissions):
|
| 2491 |
+
return NotImplemented
|
| 2492 |
+
|
| 2493 |
+
return (
|
| 2494 |
+
self.authority == other.authority
|
| 2495 |
+
and self._admissions == other._admissions
|
| 2496 |
+
)
|
| 2497 |
+
|
| 2498 |
+
def __hash__(self) -> int:
|
| 2499 |
+
return hash((self.authority, tuple(self._admissions)))
|
| 2500 |
+
|
| 2501 |
+
def public_bytes(self) -> bytes:
|
| 2502 |
+
return rust_x509.encode_extension_value(self)
|
| 2503 |
+
|
| 2504 |
+
|
| 2505 |
+
class UnrecognizedExtension(ExtensionType):
|
| 2506 |
+
def __init__(self, oid: ObjectIdentifier, value: bytes) -> None:
|
| 2507 |
+
if not isinstance(oid, ObjectIdentifier):
|
| 2508 |
+
raise TypeError("oid must be an ObjectIdentifier")
|
| 2509 |
+
self._oid = oid
|
| 2510 |
+
self._value = value
|
| 2511 |
+
|
| 2512 |
+
@property
|
| 2513 |
+
def oid(self) -> ObjectIdentifier: # type: ignore[override]
|
| 2514 |
+
return self._oid
|
| 2515 |
+
|
| 2516 |
+
@property
|
| 2517 |
+
def value(self) -> bytes:
|
| 2518 |
+
return self._value
|
| 2519 |
+
|
| 2520 |
+
def __repr__(self) -> str:
|
| 2521 |
+
return f"<UnrecognizedExtension(oid={self.oid}, value={self.value!r})>"
|
| 2522 |
+
|
| 2523 |
+
def __eq__(self, other: object) -> bool:
|
| 2524 |
+
if not isinstance(other, UnrecognizedExtension):
|
| 2525 |
+
return NotImplemented
|
| 2526 |
+
|
| 2527 |
+
return self.oid == other.oid and self.value == other.value
|
| 2528 |
+
|
| 2529 |
+
def __hash__(self) -> int:
|
| 2530 |
+
return hash((self.oid, self.value))
|
| 2531 |
+
|
| 2532 |
+
def public_bytes(self) -> bytes:
|
| 2533 |
+
return self.value
|
.mcp-venv/lib/python3.13/site-packages/cryptography/x509/general_name.py
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import abc
|
| 8 |
+
import ipaddress
|
| 9 |
+
import typing
|
| 10 |
+
from email.utils import parseaddr
|
| 11 |
+
|
| 12 |
+
from cryptography.x509.name import Name
|
| 13 |
+
from cryptography.x509.oid import ObjectIdentifier
|
| 14 |
+
|
| 15 |
+
_IPAddressTypes = typing.Union[
|
| 16 |
+
ipaddress.IPv4Address,
|
| 17 |
+
ipaddress.IPv6Address,
|
| 18 |
+
ipaddress.IPv4Network,
|
| 19 |
+
ipaddress.IPv6Network,
|
| 20 |
+
]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class UnsupportedGeneralNameType(Exception):
|
| 24 |
+
pass
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class GeneralName(metaclass=abc.ABCMeta):
|
| 28 |
+
@property
|
| 29 |
+
@abc.abstractmethod
|
| 30 |
+
def value(self) -> typing.Any:
|
| 31 |
+
"""
|
| 32 |
+
Return the value of the object
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class RFC822Name(GeneralName):
|
| 37 |
+
def __init__(self, value: str) -> None:
|
| 38 |
+
if isinstance(value, str):
|
| 39 |
+
try:
|
| 40 |
+
value.encode("ascii")
|
| 41 |
+
except UnicodeEncodeError:
|
| 42 |
+
raise ValueError(
|
| 43 |
+
"RFC822Name values should be passed as an A-label string. "
|
| 44 |
+
"This means unicode characters should be encoded via "
|
| 45 |
+
"a library like idna."
|
| 46 |
+
)
|
| 47 |
+
else:
|
| 48 |
+
raise TypeError("value must be string")
|
| 49 |
+
|
| 50 |
+
name, address = parseaddr(value)
|
| 51 |
+
if name or not address:
|
| 52 |
+
# parseaddr has found a name (e.g. Name <email>) or the entire
|
| 53 |
+
# value is an empty string.
|
| 54 |
+
raise ValueError("Invalid rfc822name value")
|
| 55 |
+
|
| 56 |
+
self._value = value
|
| 57 |
+
|
| 58 |
+
@property
|
| 59 |
+
def value(self) -> str:
|
| 60 |
+
return self._value
|
| 61 |
+
|
| 62 |
+
@classmethod
|
| 63 |
+
def _init_without_validation(cls, value: str) -> RFC822Name:
|
| 64 |
+
instance = cls.__new__(cls)
|
| 65 |
+
instance._value = value
|
| 66 |
+
return instance
|
| 67 |
+
|
| 68 |
+
def __repr__(self) -> str:
|
| 69 |
+
return f"<RFC822Name(value={self.value!r})>"
|
| 70 |
+
|
| 71 |
+
def __eq__(self, other: object) -> bool:
|
| 72 |
+
if not isinstance(other, RFC822Name):
|
| 73 |
+
return NotImplemented
|
| 74 |
+
|
| 75 |
+
return self.value == other.value
|
| 76 |
+
|
| 77 |
+
def __hash__(self) -> int:
|
| 78 |
+
return hash(self.value)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class DNSName(GeneralName):
|
| 82 |
+
def __init__(self, value: str) -> None:
|
| 83 |
+
if isinstance(value, str):
|
| 84 |
+
try:
|
| 85 |
+
value.encode("ascii")
|
| 86 |
+
except UnicodeEncodeError:
|
| 87 |
+
raise ValueError(
|
| 88 |
+
"DNSName values should be passed as an A-label string. "
|
| 89 |
+
"This means unicode characters should be encoded via "
|
| 90 |
+
"a library like idna."
|
| 91 |
+
)
|
| 92 |
+
else:
|
| 93 |
+
raise TypeError("value must be string")
|
| 94 |
+
|
| 95 |
+
self._value = value
|
| 96 |
+
|
| 97 |
+
@property
|
| 98 |
+
def value(self) -> str:
|
| 99 |
+
return self._value
|
| 100 |
+
|
| 101 |
+
@classmethod
|
| 102 |
+
def _init_without_validation(cls, value: str) -> DNSName:
|
| 103 |
+
instance = cls.__new__(cls)
|
| 104 |
+
instance._value = value
|
| 105 |
+
return instance
|
| 106 |
+
|
| 107 |
+
def __repr__(self) -> str:
|
| 108 |
+
return f"<DNSName(value={self.value!r})>"
|
| 109 |
+
|
| 110 |
+
def __eq__(self, other: object) -> bool:
|
| 111 |
+
if not isinstance(other, DNSName):
|
| 112 |
+
return NotImplemented
|
| 113 |
+
|
| 114 |
+
return self.value == other.value
|
| 115 |
+
|
| 116 |
+
def __hash__(self) -> int:
|
| 117 |
+
return hash(self.value)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class UniformResourceIdentifier(GeneralName):
|
| 121 |
+
def __init__(self, value: str) -> None:
|
| 122 |
+
if isinstance(value, str):
|
| 123 |
+
try:
|
| 124 |
+
value.encode("ascii")
|
| 125 |
+
except UnicodeEncodeError:
|
| 126 |
+
raise ValueError(
|
| 127 |
+
"URI values should be passed as an A-label string. "
|
| 128 |
+
"This means unicode characters should be encoded via "
|
| 129 |
+
"a library like idna."
|
| 130 |
+
)
|
| 131 |
+
else:
|
| 132 |
+
raise TypeError("value must be string")
|
| 133 |
+
|
| 134 |
+
self._value = value
|
| 135 |
+
|
| 136 |
+
@property
|
| 137 |
+
def value(self) -> str:
|
| 138 |
+
return self._value
|
| 139 |
+
|
| 140 |
+
@classmethod
|
| 141 |
+
def _init_without_validation(cls, value: str) -> UniformResourceIdentifier:
|
| 142 |
+
instance = cls.__new__(cls)
|
| 143 |
+
instance._value = value
|
| 144 |
+
return instance
|
| 145 |
+
|
| 146 |
+
def __repr__(self) -> str:
|
| 147 |
+
return f"<UniformResourceIdentifier(value={self.value!r})>"
|
| 148 |
+
|
| 149 |
+
def __eq__(self, other: object) -> bool:
|
| 150 |
+
if not isinstance(other, UniformResourceIdentifier):
|
| 151 |
+
return NotImplemented
|
| 152 |
+
|
| 153 |
+
return self.value == other.value
|
| 154 |
+
|
| 155 |
+
def __hash__(self) -> int:
|
| 156 |
+
return hash(self.value)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
class DirectoryName(GeneralName):
|
| 160 |
+
def __init__(self, value: Name) -> None:
|
| 161 |
+
if not isinstance(value, Name):
|
| 162 |
+
raise TypeError("value must be a Name")
|
| 163 |
+
|
| 164 |
+
self._value = value
|
| 165 |
+
|
| 166 |
+
@property
|
| 167 |
+
def value(self) -> Name:
|
| 168 |
+
return self._value
|
| 169 |
+
|
| 170 |
+
def __repr__(self) -> str:
|
| 171 |
+
return f"<DirectoryName(value={self.value})>"
|
| 172 |
+
|
| 173 |
+
def __eq__(self, other: object) -> bool:
|
| 174 |
+
if not isinstance(other, DirectoryName):
|
| 175 |
+
return NotImplemented
|
| 176 |
+
|
| 177 |
+
return self.value == other.value
|
| 178 |
+
|
| 179 |
+
def __hash__(self) -> int:
|
| 180 |
+
return hash(self.value)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
class RegisteredID(GeneralName):
|
| 184 |
+
def __init__(self, value: ObjectIdentifier) -> None:
|
| 185 |
+
if not isinstance(value, ObjectIdentifier):
|
| 186 |
+
raise TypeError("value must be an ObjectIdentifier")
|
| 187 |
+
|
| 188 |
+
self._value = value
|
| 189 |
+
|
| 190 |
+
@property
|
| 191 |
+
def value(self) -> ObjectIdentifier:
|
| 192 |
+
return self._value
|
| 193 |
+
|
| 194 |
+
def __repr__(self) -> str:
|
| 195 |
+
return f"<RegisteredID(value={self.value})>"
|
| 196 |
+
|
| 197 |
+
def __eq__(self, other: object) -> bool:
|
| 198 |
+
if not isinstance(other, RegisteredID):
|
| 199 |
+
return NotImplemented
|
| 200 |
+
|
| 201 |
+
return self.value == other.value
|
| 202 |
+
|
| 203 |
+
def __hash__(self) -> int:
|
| 204 |
+
return hash(self.value)
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
class IPAddress(GeneralName):
|
| 208 |
+
def __init__(self, value: _IPAddressTypes) -> None:
|
| 209 |
+
if not isinstance(
|
| 210 |
+
value,
|
| 211 |
+
(
|
| 212 |
+
ipaddress.IPv4Address,
|
| 213 |
+
ipaddress.IPv6Address,
|
| 214 |
+
ipaddress.IPv4Network,
|
| 215 |
+
ipaddress.IPv6Network,
|
| 216 |
+
),
|
| 217 |
+
):
|
| 218 |
+
raise TypeError(
|
| 219 |
+
"value must be an instance of ipaddress.IPv4Address, "
|
| 220 |
+
"ipaddress.IPv6Address, ipaddress.IPv4Network, or "
|
| 221 |
+
"ipaddress.IPv6Network"
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
self._value = value
|
| 225 |
+
|
| 226 |
+
@property
|
| 227 |
+
def value(self) -> _IPAddressTypes:
|
| 228 |
+
return self._value
|
| 229 |
+
|
| 230 |
+
def _packed(self) -> bytes:
|
| 231 |
+
if isinstance(
|
| 232 |
+
self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address)
|
| 233 |
+
):
|
| 234 |
+
return self.value.packed
|
| 235 |
+
else:
|
| 236 |
+
return (
|
| 237 |
+
self.value.network_address.packed + self.value.netmask.packed
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
def __repr__(self) -> str:
|
| 241 |
+
return f"<IPAddress(value={self.value})>"
|
| 242 |
+
|
| 243 |
+
def __eq__(self, other: object) -> bool:
|
| 244 |
+
if not isinstance(other, IPAddress):
|
| 245 |
+
return NotImplemented
|
| 246 |
+
|
| 247 |
+
return self.value == other.value
|
| 248 |
+
|
| 249 |
+
def __hash__(self) -> int:
|
| 250 |
+
return hash(self.value)
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
class OtherName(GeneralName):
|
| 254 |
+
def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None:
|
| 255 |
+
if not isinstance(type_id, ObjectIdentifier):
|
| 256 |
+
raise TypeError("type_id must be an ObjectIdentifier")
|
| 257 |
+
if not isinstance(value, bytes):
|
| 258 |
+
raise TypeError("value must be a binary string")
|
| 259 |
+
|
| 260 |
+
self._type_id = type_id
|
| 261 |
+
self._value = value
|
| 262 |
+
|
| 263 |
+
@property
|
| 264 |
+
def type_id(self) -> ObjectIdentifier:
|
| 265 |
+
return self._type_id
|
| 266 |
+
|
| 267 |
+
@property
|
| 268 |
+
def value(self) -> bytes:
|
| 269 |
+
return self._value
|
| 270 |
+
|
| 271 |
+
def __repr__(self) -> str:
|
| 272 |
+
return f"<OtherName(type_id={self.type_id}, value={self.value!r})>"
|
| 273 |
+
|
| 274 |
+
def __eq__(self, other: object) -> bool:
|
| 275 |
+
if not isinstance(other, OtherName):
|
| 276 |
+
return NotImplemented
|
| 277 |
+
|
| 278 |
+
return self.type_id == other.type_id and self.value == other.value
|
| 279 |
+
|
| 280 |
+
def __hash__(self) -> int:
|
| 281 |
+
return hash((self.type_id, self.value))
|
.mcp-venv/lib/python3.13/site-packages/cryptography/x509/name.py
ADDED
|
@@ -0,0 +1,489 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import binascii
|
| 8 |
+
import re
|
| 9 |
+
import sys
|
| 10 |
+
import typing
|
| 11 |
+
import warnings
|
| 12 |
+
from collections.abc import Iterable, Iterator
|
| 13 |
+
|
| 14 |
+
from cryptography import utils
|
| 15 |
+
from cryptography.hazmat.bindings._rust import x509 as rust_x509
|
| 16 |
+
from cryptography.x509.oid import NameOID, ObjectIdentifier
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class _ASN1Type(utils.Enum):
|
| 20 |
+
BitString = 3
|
| 21 |
+
OctetString = 4
|
| 22 |
+
UTF8String = 12
|
| 23 |
+
NumericString = 18
|
| 24 |
+
PrintableString = 19
|
| 25 |
+
T61String = 20
|
| 26 |
+
IA5String = 22
|
| 27 |
+
UTCTime = 23
|
| 28 |
+
GeneralizedTime = 24
|
| 29 |
+
VisibleString = 26
|
| 30 |
+
UniversalString = 28
|
| 31 |
+
BMPString = 30
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type}
|
| 35 |
+
_NAMEOID_DEFAULT_TYPE: dict[ObjectIdentifier, _ASN1Type] = {
|
| 36 |
+
NameOID.COUNTRY_NAME: _ASN1Type.PrintableString,
|
| 37 |
+
NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString,
|
| 38 |
+
NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString,
|
| 39 |
+
NameOID.DN_QUALIFIER: _ASN1Type.PrintableString,
|
| 40 |
+
NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String,
|
| 41 |
+
NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String,
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
# Type alias
|
| 45 |
+
_OidNameMap = typing.Mapping[ObjectIdentifier, str]
|
| 46 |
+
_NameOidMap = typing.Mapping[str, ObjectIdentifier]
|
| 47 |
+
|
| 48 |
+
#: Short attribute names from RFC 4514:
|
| 49 |
+
#: https://tools.ietf.org/html/rfc4514#page-7
|
| 50 |
+
_NAMEOID_TO_NAME: _OidNameMap = {
|
| 51 |
+
NameOID.COMMON_NAME: "CN",
|
| 52 |
+
NameOID.LOCALITY_NAME: "L",
|
| 53 |
+
NameOID.STATE_OR_PROVINCE_NAME: "ST",
|
| 54 |
+
NameOID.ORGANIZATION_NAME: "O",
|
| 55 |
+
NameOID.ORGANIZATIONAL_UNIT_NAME: "OU",
|
| 56 |
+
NameOID.COUNTRY_NAME: "C",
|
| 57 |
+
NameOID.STREET_ADDRESS: "STREET",
|
| 58 |
+
NameOID.DOMAIN_COMPONENT: "DC",
|
| 59 |
+
NameOID.USER_ID: "UID",
|
| 60 |
+
}
|
| 61 |
+
_NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()}
|
| 62 |
+
|
| 63 |
+
_NAMEOID_LENGTH_LIMIT = {
|
| 64 |
+
NameOID.COUNTRY_NAME: (2, 2),
|
| 65 |
+
NameOID.JURISDICTION_COUNTRY_NAME: (2, 2),
|
| 66 |
+
NameOID.COMMON_NAME: (1, 64),
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def _escape_dn_value(val: str | bytes) -> str:
|
| 71 |
+
"""Escape special characters in RFC4514 Distinguished Name value."""
|
| 72 |
+
|
| 73 |
+
if not val:
|
| 74 |
+
return ""
|
| 75 |
+
|
| 76 |
+
# RFC 4514 Section 2.4 defines the value as being the # (U+0023) character
|
| 77 |
+
# followed by the hexadecimal encoding of the octets.
|
| 78 |
+
if isinstance(val, bytes):
|
| 79 |
+
return "#" + binascii.hexlify(val).decode("utf8")
|
| 80 |
+
|
| 81 |
+
# See https://tools.ietf.org/html/rfc4514#section-2.4
|
| 82 |
+
val = val.replace("\\", "\\\\")
|
| 83 |
+
val = val.replace('"', '\\"')
|
| 84 |
+
val = val.replace("+", "\\+")
|
| 85 |
+
val = val.replace(",", "\\,")
|
| 86 |
+
val = val.replace(";", "\\;")
|
| 87 |
+
val = val.replace("<", "\\<")
|
| 88 |
+
val = val.replace(">", "\\>")
|
| 89 |
+
val = val.replace("\0", "\\00")
|
| 90 |
+
|
| 91 |
+
if val[0] == "#" or (val[0] == " " and len(val) > 1):
|
| 92 |
+
val = "\\" + val
|
| 93 |
+
if val[-1] == " ":
|
| 94 |
+
val = val[:-1] + "\\ "
|
| 95 |
+
|
| 96 |
+
return val
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def _unescape_dn_value(val: str) -> str:
|
| 100 |
+
if not val:
|
| 101 |
+
return ""
|
| 102 |
+
|
| 103 |
+
# See https://tools.ietf.org/html/rfc4514#section-3
|
| 104 |
+
|
| 105 |
+
# special = escaped / SPACE / SHARP / EQUALS
|
| 106 |
+
# escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
|
| 107 |
+
def sub(m):
|
| 108 |
+
val = m.group(0)
|
| 109 |
+
# Special character escape
|
| 110 |
+
if len(val) == 2:
|
| 111 |
+
return val[1:]
|
| 112 |
+
|
| 113 |
+
# Unicode string of hex
|
| 114 |
+
return binascii.unhexlify(val.replace("\\", "")).decode()
|
| 115 |
+
|
| 116 |
+
return _RFC4514NameParser._PAIR_MULTI_RE.sub(sub, val)
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
NameAttributeValueType = typing.TypeVar(
|
| 120 |
+
"NameAttributeValueType",
|
| 121 |
+
typing.Union[str, bytes],
|
| 122 |
+
str,
|
| 123 |
+
bytes,
|
| 124 |
+
covariant=True,
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
class NameAttribute(typing.Generic[NameAttributeValueType]):
|
| 129 |
+
def __init__(
|
| 130 |
+
self,
|
| 131 |
+
oid: ObjectIdentifier,
|
| 132 |
+
value: NameAttributeValueType,
|
| 133 |
+
_type: _ASN1Type | None = None,
|
| 134 |
+
*,
|
| 135 |
+
_validate: bool = True,
|
| 136 |
+
) -> None:
|
| 137 |
+
if not isinstance(oid, ObjectIdentifier):
|
| 138 |
+
raise TypeError(
|
| 139 |
+
"oid argument must be an ObjectIdentifier instance."
|
| 140 |
+
)
|
| 141 |
+
if _type == _ASN1Type.BitString:
|
| 142 |
+
if oid != NameOID.X500_UNIQUE_IDENTIFIER:
|
| 143 |
+
raise TypeError(
|
| 144 |
+
"oid must be X500_UNIQUE_IDENTIFIER for BitString type."
|
| 145 |
+
)
|
| 146 |
+
if not isinstance(value, bytes):
|
| 147 |
+
raise TypeError("value must be bytes for BitString")
|
| 148 |
+
elif not isinstance(value, str):
|
| 149 |
+
raise TypeError("value argument must be a str")
|
| 150 |
+
|
| 151 |
+
length_limits = _NAMEOID_LENGTH_LIMIT.get(oid)
|
| 152 |
+
if length_limits is not None:
|
| 153 |
+
min_length, max_length = length_limits
|
| 154 |
+
assert isinstance(value, str)
|
| 155 |
+
c_len = len(value.encode("utf8"))
|
| 156 |
+
if c_len < min_length or c_len > max_length:
|
| 157 |
+
msg = (
|
| 158 |
+
f"Attribute's length must be >= {min_length} and "
|
| 159 |
+
f"<= {max_length}, but it was {c_len}"
|
| 160 |
+
)
|
| 161 |
+
if _validate is True:
|
| 162 |
+
raise ValueError(msg)
|
| 163 |
+
else:
|
| 164 |
+
warnings.warn(msg, stacklevel=2)
|
| 165 |
+
|
| 166 |
+
# The appropriate ASN1 string type varies by OID and is defined across
|
| 167 |
+
# multiple RFCs including 2459, 3280, and 5280. In general UTF8String
|
| 168 |
+
# is preferred (2459), but 3280 and 5280 specify several OIDs with
|
| 169 |
+
# alternate types. This means when we see the sentinel value we need
|
| 170 |
+
# to look up whether the OID has a non-UTF8 type. If it does, set it
|
| 171 |
+
# to that. Otherwise, UTF8!
|
| 172 |
+
if _type is None:
|
| 173 |
+
_type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String)
|
| 174 |
+
|
| 175 |
+
if not isinstance(_type, _ASN1Type):
|
| 176 |
+
raise TypeError("_type must be from the _ASN1Type enum")
|
| 177 |
+
|
| 178 |
+
self._oid = oid
|
| 179 |
+
self._value: NameAttributeValueType = value
|
| 180 |
+
self._type: _ASN1Type = _type
|
| 181 |
+
|
| 182 |
+
@property
|
| 183 |
+
def oid(self) -> ObjectIdentifier:
|
| 184 |
+
return self._oid
|
| 185 |
+
|
| 186 |
+
@property
|
| 187 |
+
def value(self) -> NameAttributeValueType:
|
| 188 |
+
return self._value
|
| 189 |
+
|
| 190 |
+
@property
|
| 191 |
+
def rfc4514_attribute_name(self) -> str:
|
| 192 |
+
"""
|
| 193 |
+
The short attribute name (for example "CN") if available,
|
| 194 |
+
otherwise the OID dotted string.
|
| 195 |
+
"""
|
| 196 |
+
return _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string)
|
| 197 |
+
|
| 198 |
+
def rfc4514_string(
|
| 199 |
+
self, attr_name_overrides: _OidNameMap | None = None
|
| 200 |
+
) -> str:
|
| 201 |
+
"""
|
| 202 |
+
Format as RFC4514 Distinguished Name string.
|
| 203 |
+
|
| 204 |
+
Use short attribute name if available, otherwise fall back to OID
|
| 205 |
+
dotted string.
|
| 206 |
+
"""
|
| 207 |
+
attr_name = (
|
| 208 |
+
attr_name_overrides.get(self.oid) if attr_name_overrides else None
|
| 209 |
+
)
|
| 210 |
+
if attr_name is None:
|
| 211 |
+
attr_name = self.rfc4514_attribute_name
|
| 212 |
+
|
| 213 |
+
return f"{attr_name}={_escape_dn_value(self.value)}"
|
| 214 |
+
|
| 215 |
+
def __eq__(self, other: object) -> bool:
|
| 216 |
+
if not isinstance(other, NameAttribute):
|
| 217 |
+
return NotImplemented
|
| 218 |
+
|
| 219 |
+
return self.oid == other.oid and self.value == other.value
|
| 220 |
+
|
| 221 |
+
def __hash__(self) -> int:
|
| 222 |
+
return hash((self.oid, self.value))
|
| 223 |
+
|
| 224 |
+
def __repr__(self) -> str:
|
| 225 |
+
return f"<NameAttribute(oid={self.oid}, value={self.value!r})>"
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
class RelativeDistinguishedName:
|
| 229 |
+
def __init__(self, attributes: Iterable[NameAttribute[str | bytes]]):
|
| 230 |
+
attributes = list(attributes)
|
| 231 |
+
if not attributes:
|
| 232 |
+
raise ValueError("a relative distinguished name cannot be empty")
|
| 233 |
+
if not all(isinstance(x, NameAttribute) for x in attributes):
|
| 234 |
+
raise TypeError("attributes must be an iterable of NameAttribute")
|
| 235 |
+
|
| 236 |
+
# Keep list and frozenset to preserve attribute order where it matters
|
| 237 |
+
self._attributes = attributes
|
| 238 |
+
self._attribute_set = frozenset(attributes)
|
| 239 |
+
|
| 240 |
+
if len(self._attribute_set) != len(attributes):
|
| 241 |
+
raise ValueError("duplicate attributes are not allowed")
|
| 242 |
+
|
| 243 |
+
def get_attributes_for_oid(
|
| 244 |
+
self,
|
| 245 |
+
oid: ObjectIdentifier,
|
| 246 |
+
) -> list[NameAttribute[str | bytes]]:
|
| 247 |
+
return [i for i in self if i.oid == oid]
|
| 248 |
+
|
| 249 |
+
def rfc4514_string(
|
| 250 |
+
self, attr_name_overrides: _OidNameMap | None = None
|
| 251 |
+
) -> str:
|
| 252 |
+
"""
|
| 253 |
+
Format as RFC4514 Distinguished Name string.
|
| 254 |
+
|
| 255 |
+
Within each RDN, attributes are joined by '+', although that is rarely
|
| 256 |
+
used in certificates.
|
| 257 |
+
"""
|
| 258 |
+
return "+".join(
|
| 259 |
+
attr.rfc4514_string(attr_name_overrides)
|
| 260 |
+
for attr in self._attributes
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
def __eq__(self, other: object) -> bool:
|
| 264 |
+
if not isinstance(other, RelativeDistinguishedName):
|
| 265 |
+
return NotImplemented
|
| 266 |
+
|
| 267 |
+
return self._attribute_set == other._attribute_set
|
| 268 |
+
|
| 269 |
+
def __hash__(self) -> int:
|
| 270 |
+
return hash(self._attribute_set)
|
| 271 |
+
|
| 272 |
+
def __iter__(self) -> Iterator[NameAttribute[str | bytes]]:
|
| 273 |
+
return iter(self._attributes)
|
| 274 |
+
|
| 275 |
+
def __len__(self) -> int:
|
| 276 |
+
return len(self._attributes)
|
| 277 |
+
|
| 278 |
+
def __repr__(self) -> str:
|
| 279 |
+
return f"<RelativeDistinguishedName({self.rfc4514_string()})>"
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
class Name:
|
| 283 |
+
@typing.overload
|
| 284 |
+
def __init__(
|
| 285 |
+
self, attributes: Iterable[NameAttribute[str | bytes]]
|
| 286 |
+
) -> None: ...
|
| 287 |
+
|
| 288 |
+
@typing.overload
|
| 289 |
+
def __init__(
|
| 290 |
+
self, attributes: Iterable[RelativeDistinguishedName]
|
| 291 |
+
) -> None: ...
|
| 292 |
+
|
| 293 |
+
def __init__(
|
| 294 |
+
self,
|
| 295 |
+
attributes: Iterable[
|
| 296 |
+
NameAttribute[str | bytes] | RelativeDistinguishedName
|
| 297 |
+
],
|
| 298 |
+
) -> None:
|
| 299 |
+
attributes = list(attributes)
|
| 300 |
+
if all(isinstance(x, NameAttribute) for x in attributes):
|
| 301 |
+
self._attributes = [
|
| 302 |
+
RelativeDistinguishedName([typing.cast(NameAttribute, x)])
|
| 303 |
+
for x in attributes
|
| 304 |
+
]
|
| 305 |
+
elif all(isinstance(x, RelativeDistinguishedName) for x in attributes):
|
| 306 |
+
self._attributes = typing.cast(
|
| 307 |
+
list[RelativeDistinguishedName], attributes
|
| 308 |
+
)
|
| 309 |
+
else:
|
| 310 |
+
raise TypeError(
|
| 311 |
+
"attributes must be a list of NameAttribute"
|
| 312 |
+
" or a list RelativeDistinguishedName"
|
| 313 |
+
)
|
| 314 |
+
|
| 315 |
+
@classmethod
|
| 316 |
+
def from_rfc4514_string(
|
| 317 |
+
cls,
|
| 318 |
+
data: str,
|
| 319 |
+
attr_name_overrides: _NameOidMap | None = None,
|
| 320 |
+
) -> Name:
|
| 321 |
+
return _RFC4514NameParser(data, attr_name_overrides or {}).parse()
|
| 322 |
+
|
| 323 |
+
@classmethod
|
| 324 |
+
def from_bytes(cls, data: bytes) -> Name:
|
| 325 |
+
"""
|
| 326 |
+
Parse a DER encoded X.509 name (the inverse of ``public_bytes``).
|
| 327 |
+
"""
|
| 328 |
+
return rust_x509.parse_name_bytes(data)
|
| 329 |
+
|
| 330 |
+
def rfc4514_string(
|
| 331 |
+
self, attr_name_overrides: _OidNameMap | None = None
|
| 332 |
+
) -> str:
|
| 333 |
+
"""
|
| 334 |
+
Format as RFC4514 Distinguished Name string.
|
| 335 |
+
For example 'CN=foobar.com,O=Foo Corp,C=US'
|
| 336 |
+
|
| 337 |
+
An X.509 name is a two-level structure: a list of sets of attributes.
|
| 338 |
+
Each list element is separated by ',' and within each list element, set
|
| 339 |
+
elements are separated by '+'. The latter is almost never used in
|
| 340 |
+
real world certificates. According to RFC4514 section 2.1 the
|
| 341 |
+
RDNSequence must be reversed when converting to string representation.
|
| 342 |
+
"""
|
| 343 |
+
return ",".join(
|
| 344 |
+
attr.rfc4514_string(attr_name_overrides)
|
| 345 |
+
for attr in reversed(self._attributes)
|
| 346 |
+
)
|
| 347 |
+
|
| 348 |
+
def get_attributes_for_oid(
|
| 349 |
+
self,
|
| 350 |
+
oid: ObjectIdentifier,
|
| 351 |
+
) -> list[NameAttribute[str | bytes]]:
|
| 352 |
+
return [i for i in self if i.oid == oid]
|
| 353 |
+
|
| 354 |
+
@property
|
| 355 |
+
def rdns(self) -> list[RelativeDistinguishedName]:
|
| 356 |
+
return self._attributes
|
| 357 |
+
|
| 358 |
+
def public_bytes(self, backend: typing.Any = None) -> bytes:
|
| 359 |
+
return rust_x509.encode_name_bytes(self)
|
| 360 |
+
|
| 361 |
+
def __eq__(self, other: object) -> bool:
|
| 362 |
+
if not isinstance(other, Name):
|
| 363 |
+
return NotImplemented
|
| 364 |
+
|
| 365 |
+
return self._attributes == other._attributes
|
| 366 |
+
|
| 367 |
+
def __hash__(self) -> int:
|
| 368 |
+
# TODO: this is relatively expensive, if this looks like a bottleneck
|
| 369 |
+
# for you, consider optimizing!
|
| 370 |
+
return hash(tuple(self._attributes))
|
| 371 |
+
|
| 372 |
+
def __iter__(self) -> Iterator[NameAttribute[str | bytes]]:
|
| 373 |
+
for rdn in self._attributes:
|
| 374 |
+
yield from rdn
|
| 375 |
+
|
| 376 |
+
def __len__(self) -> int:
|
| 377 |
+
return sum(len(rdn) for rdn in self._attributes)
|
| 378 |
+
|
| 379 |
+
def __repr__(self) -> str:
|
| 380 |
+
return f"<Name({self.rfc4514_string()})>"
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
class _RFC4514NameParser:
|
| 384 |
+
_OID_RE = re.compile(r"(0|([1-9]\d*))(\.(0|([1-9]\d*)))+")
|
| 385 |
+
_DESCR_RE = re.compile(r"[a-zA-Z][a-zA-Z\d-]*")
|
| 386 |
+
|
| 387 |
+
_ESCAPE_SPECIAL = r"[\\ #=\"\+,;<>]"
|
| 388 |
+
_ESCAPE_HEX = r"[\da-zA-Z]{2}"
|
| 389 |
+
_PAIR = rf"\\({_ESCAPE_SPECIAL}|{_ESCAPE_HEX})"
|
| 390 |
+
_PAIR_MULTI_RE = re.compile(rf"(\\{_ESCAPE_SPECIAL})|((\\{_ESCAPE_HEX})+)")
|
| 391 |
+
_LUTF1 = r"[\x01-\x1f\x21\x24-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]"
|
| 392 |
+
_SUTF1 = r"[\x01-\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]"
|
| 393 |
+
_TUTF1 = r"[\x01-\x1F\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]"
|
| 394 |
+
_UTFMB = rf"[\x80-{chr(sys.maxunicode)}]"
|
| 395 |
+
_LEADCHAR = rf"{_LUTF1}|{_UTFMB}"
|
| 396 |
+
_STRINGCHAR = rf"{_SUTF1}|{_UTFMB}"
|
| 397 |
+
_TRAILCHAR = rf"{_TUTF1}|{_UTFMB}"
|
| 398 |
+
_STRING_RE = re.compile(
|
| 399 |
+
rf"""
|
| 400 |
+
(
|
| 401 |
+
({_LEADCHAR}|{_PAIR})
|
| 402 |
+
(
|
| 403 |
+
({_STRINGCHAR}|{_PAIR})*
|
| 404 |
+
({_TRAILCHAR}|{_PAIR})
|
| 405 |
+
)?
|
| 406 |
+
)?
|
| 407 |
+
""",
|
| 408 |
+
re.VERBOSE,
|
| 409 |
+
)
|
| 410 |
+
_HEXSTRING_RE = re.compile(r"#([\da-zA-Z]{2})+")
|
| 411 |
+
|
| 412 |
+
def __init__(self, data: str, attr_name_overrides: _NameOidMap) -> None:
|
| 413 |
+
self._data = data
|
| 414 |
+
self._idx = 0
|
| 415 |
+
|
| 416 |
+
self._attr_name_overrides = attr_name_overrides
|
| 417 |
+
|
| 418 |
+
def _has_data(self) -> bool:
|
| 419 |
+
return self._idx < len(self._data)
|
| 420 |
+
|
| 421 |
+
def _peek(self) -> str | None:
|
| 422 |
+
if self._has_data():
|
| 423 |
+
return self._data[self._idx]
|
| 424 |
+
return None
|
| 425 |
+
|
| 426 |
+
def _read_char(self, ch: str) -> None:
|
| 427 |
+
if self._peek() != ch:
|
| 428 |
+
raise ValueError
|
| 429 |
+
self._idx += 1
|
| 430 |
+
|
| 431 |
+
def _read_re(self, pat) -> str:
|
| 432 |
+
match = pat.match(self._data, pos=self._idx)
|
| 433 |
+
if match is None:
|
| 434 |
+
raise ValueError
|
| 435 |
+
val = match.group()
|
| 436 |
+
self._idx += len(val)
|
| 437 |
+
return val
|
| 438 |
+
|
| 439 |
+
def parse(self) -> Name:
|
| 440 |
+
"""
|
| 441 |
+
Parses the `data` string and converts it to a Name.
|
| 442 |
+
|
| 443 |
+
According to RFC4514 section 2.1 the RDNSequence must be
|
| 444 |
+
reversed when converting to string representation. So, when
|
| 445 |
+
we parse it, we need to reverse again to get the RDNs on the
|
| 446 |
+
correct order.
|
| 447 |
+
"""
|
| 448 |
+
|
| 449 |
+
if not self._has_data():
|
| 450 |
+
return Name([])
|
| 451 |
+
|
| 452 |
+
rdns = [self._parse_rdn()]
|
| 453 |
+
|
| 454 |
+
while self._has_data():
|
| 455 |
+
self._read_char(",")
|
| 456 |
+
rdns.append(self._parse_rdn())
|
| 457 |
+
|
| 458 |
+
return Name(reversed(rdns))
|
| 459 |
+
|
| 460 |
+
def _parse_rdn(self) -> RelativeDistinguishedName:
|
| 461 |
+
nas = [self._parse_na()]
|
| 462 |
+
while self._peek() == "+":
|
| 463 |
+
self._read_char("+")
|
| 464 |
+
nas.append(self._parse_na())
|
| 465 |
+
|
| 466 |
+
return RelativeDistinguishedName(nas)
|
| 467 |
+
|
| 468 |
+
def _parse_na(self) -> NameAttribute[str]:
|
| 469 |
+
try:
|
| 470 |
+
oid_value = self._read_re(self._OID_RE)
|
| 471 |
+
except ValueError:
|
| 472 |
+
name = self._read_re(self._DESCR_RE)
|
| 473 |
+
oid = self._attr_name_overrides.get(
|
| 474 |
+
name, _NAME_TO_NAMEOID.get(name)
|
| 475 |
+
)
|
| 476 |
+
if oid is None:
|
| 477 |
+
raise ValueError
|
| 478 |
+
else:
|
| 479 |
+
oid = ObjectIdentifier(oid_value)
|
| 480 |
+
|
| 481 |
+
self._read_char("=")
|
| 482 |
+
if self._peek() == "#":
|
| 483 |
+
value = self._read_re(self._HEXSTRING_RE)
|
| 484 |
+
value = binascii.unhexlify(value[1:]).decode()
|
| 485 |
+
else:
|
| 486 |
+
raw_value = self._read_re(self._STRING_RE)
|
| 487 |
+
value = _unescape_dn_value(raw_value)
|
| 488 |
+
|
| 489 |
+
return NameAttribute(oid, value)
|
.mcp-venv/lib/python3.13/site-packages/cryptography/x509/ocsp.py
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import datetime
|
| 8 |
+
from collections.abc import Iterable
|
| 9 |
+
|
| 10 |
+
from cryptography import utils, x509
|
| 11 |
+
from cryptography.hazmat.bindings._rust import ocsp
|
| 12 |
+
from cryptography.hazmat.primitives import hashes
|
| 13 |
+
from cryptography.hazmat.primitives.asymmetric.types import (
|
| 14 |
+
CertificateIssuerPrivateKeyTypes,
|
| 15 |
+
)
|
| 16 |
+
from cryptography.x509.base import _reject_duplicate_extension
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class OCSPResponderEncoding(utils.Enum):
|
| 20 |
+
HASH = "By Hash"
|
| 21 |
+
NAME = "By Name"
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class OCSPResponseStatus(utils.Enum):
|
| 25 |
+
SUCCESSFUL = 0
|
| 26 |
+
MALFORMED_REQUEST = 1
|
| 27 |
+
INTERNAL_ERROR = 2
|
| 28 |
+
TRY_LATER = 3
|
| 29 |
+
SIG_REQUIRED = 5
|
| 30 |
+
UNAUTHORIZED = 6
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
_ALLOWED_HASHES = (
|
| 34 |
+
hashes.SHA1,
|
| 35 |
+
hashes.SHA224,
|
| 36 |
+
hashes.SHA256,
|
| 37 |
+
hashes.SHA384,
|
| 38 |
+
hashes.SHA512,
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None:
|
| 43 |
+
if not isinstance(algorithm, _ALLOWED_HASHES):
|
| 44 |
+
raise ValueError(
|
| 45 |
+
"Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512"
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class OCSPCertStatus(utils.Enum):
|
| 50 |
+
GOOD = 0
|
| 51 |
+
REVOKED = 1
|
| 52 |
+
UNKNOWN = 2
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class _SingleResponse:
|
| 56 |
+
def __init__(
|
| 57 |
+
self,
|
| 58 |
+
resp: tuple[x509.Certificate, x509.Certificate] | None,
|
| 59 |
+
resp_hash: tuple[bytes, bytes, int] | None,
|
| 60 |
+
algorithm: hashes.HashAlgorithm,
|
| 61 |
+
cert_status: OCSPCertStatus,
|
| 62 |
+
this_update: datetime.datetime,
|
| 63 |
+
next_update: datetime.datetime | None,
|
| 64 |
+
revocation_time: datetime.datetime | None,
|
| 65 |
+
revocation_reason: x509.ReasonFlags | None,
|
| 66 |
+
):
|
| 67 |
+
_verify_algorithm(algorithm)
|
| 68 |
+
if not isinstance(this_update, datetime.datetime):
|
| 69 |
+
raise TypeError("this_update must be a datetime object")
|
| 70 |
+
if next_update is not None and not isinstance(
|
| 71 |
+
next_update, datetime.datetime
|
| 72 |
+
):
|
| 73 |
+
raise TypeError("next_update must be a datetime object or None")
|
| 74 |
+
|
| 75 |
+
self._resp = resp
|
| 76 |
+
self._resp_hash = resp_hash
|
| 77 |
+
self._algorithm = algorithm
|
| 78 |
+
self._this_update = this_update
|
| 79 |
+
self._next_update = next_update
|
| 80 |
+
|
| 81 |
+
if not isinstance(cert_status, OCSPCertStatus):
|
| 82 |
+
raise TypeError(
|
| 83 |
+
"cert_status must be an item from the OCSPCertStatus enum"
|
| 84 |
+
)
|
| 85 |
+
if cert_status is not OCSPCertStatus.REVOKED:
|
| 86 |
+
if revocation_time is not None:
|
| 87 |
+
raise ValueError(
|
| 88 |
+
"revocation_time can only be provided if the certificate "
|
| 89 |
+
"is revoked"
|
| 90 |
+
)
|
| 91 |
+
if revocation_reason is not None:
|
| 92 |
+
raise ValueError(
|
| 93 |
+
"revocation_reason can only be provided if the certificate"
|
| 94 |
+
" is revoked"
|
| 95 |
+
)
|
| 96 |
+
else:
|
| 97 |
+
if not isinstance(revocation_time, datetime.datetime):
|
| 98 |
+
raise TypeError("revocation_time must be a datetime object")
|
| 99 |
+
|
| 100 |
+
if revocation_reason is not None and not isinstance(
|
| 101 |
+
revocation_reason, x509.ReasonFlags
|
| 102 |
+
):
|
| 103 |
+
raise TypeError(
|
| 104 |
+
"revocation_reason must be an item from the ReasonFlags "
|
| 105 |
+
"enum or None"
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
self._cert_status = cert_status
|
| 109 |
+
self._revocation_time = revocation_time
|
| 110 |
+
self._revocation_reason = revocation_reason
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
OCSPRequest = ocsp.OCSPRequest
|
| 114 |
+
OCSPResponse = ocsp.OCSPResponse
|
| 115 |
+
OCSPSingleResponse = ocsp.OCSPSingleResponse
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
class OCSPRequestBuilder:
|
| 119 |
+
def __init__(
|
| 120 |
+
self,
|
| 121 |
+
request: tuple[
|
| 122 |
+
x509.Certificate, x509.Certificate, hashes.HashAlgorithm
|
| 123 |
+
]
|
| 124 |
+
| None = None,
|
| 125 |
+
request_hash: tuple[bytes, bytes, int, hashes.HashAlgorithm]
|
| 126 |
+
| None = None,
|
| 127 |
+
extensions: list[x509.Extension[x509.ExtensionType]] = [],
|
| 128 |
+
) -> None:
|
| 129 |
+
self._request = request
|
| 130 |
+
self._request_hash = request_hash
|
| 131 |
+
self._extensions = extensions
|
| 132 |
+
|
| 133 |
+
def add_certificate(
|
| 134 |
+
self,
|
| 135 |
+
cert: x509.Certificate,
|
| 136 |
+
issuer: x509.Certificate,
|
| 137 |
+
algorithm: hashes.HashAlgorithm,
|
| 138 |
+
) -> OCSPRequestBuilder:
|
| 139 |
+
if self._request is not None or self._request_hash is not None:
|
| 140 |
+
raise ValueError("Only one certificate can be added to a request")
|
| 141 |
+
|
| 142 |
+
_verify_algorithm(algorithm)
|
| 143 |
+
if not isinstance(cert, x509.Certificate) or not isinstance(
|
| 144 |
+
issuer, x509.Certificate
|
| 145 |
+
):
|
| 146 |
+
raise TypeError("cert and issuer must be a Certificate")
|
| 147 |
+
|
| 148 |
+
return OCSPRequestBuilder(
|
| 149 |
+
(cert, issuer, algorithm), self._request_hash, self._extensions
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
def add_certificate_by_hash(
|
| 153 |
+
self,
|
| 154 |
+
issuer_name_hash: bytes,
|
| 155 |
+
issuer_key_hash: bytes,
|
| 156 |
+
serial_number: int,
|
| 157 |
+
algorithm: hashes.HashAlgorithm,
|
| 158 |
+
) -> OCSPRequestBuilder:
|
| 159 |
+
if self._request is not None or self._request_hash is not None:
|
| 160 |
+
raise ValueError("Only one certificate can be added to a request")
|
| 161 |
+
|
| 162 |
+
if not isinstance(serial_number, int):
|
| 163 |
+
raise TypeError("serial_number must be an integer")
|
| 164 |
+
|
| 165 |
+
_verify_algorithm(algorithm)
|
| 166 |
+
utils._check_bytes("issuer_name_hash", issuer_name_hash)
|
| 167 |
+
utils._check_bytes("issuer_key_hash", issuer_key_hash)
|
| 168 |
+
if algorithm.digest_size != len(
|
| 169 |
+
issuer_name_hash
|
| 170 |
+
) or algorithm.digest_size != len(issuer_key_hash):
|
| 171 |
+
raise ValueError(
|
| 172 |
+
"issuer_name_hash and issuer_key_hash must be the same length "
|
| 173 |
+
"as the digest size of the algorithm"
|
| 174 |
+
)
|
| 175 |
+
|
| 176 |
+
return OCSPRequestBuilder(
|
| 177 |
+
self._request,
|
| 178 |
+
(issuer_name_hash, issuer_key_hash, serial_number, algorithm),
|
| 179 |
+
self._extensions,
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
def add_extension(
|
| 183 |
+
self, extval: x509.ExtensionType, critical: bool
|
| 184 |
+
) -> OCSPRequestBuilder:
|
| 185 |
+
if not isinstance(extval, x509.ExtensionType):
|
| 186 |
+
raise TypeError("extension must be an ExtensionType")
|
| 187 |
+
|
| 188 |
+
extension = x509.Extension(extval.oid, critical, extval)
|
| 189 |
+
_reject_duplicate_extension(extension, self._extensions)
|
| 190 |
+
|
| 191 |
+
return OCSPRequestBuilder(
|
| 192 |
+
self._request, self._request_hash, [*self._extensions, extension]
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
def build(self) -> OCSPRequest:
|
| 196 |
+
if self._request is None and self._request_hash is None:
|
| 197 |
+
raise ValueError("You must add a certificate before building")
|
| 198 |
+
|
| 199 |
+
return ocsp.create_ocsp_request(self)
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
class OCSPResponseBuilder:
|
| 203 |
+
def __init__(
|
| 204 |
+
self,
|
| 205 |
+
response: _SingleResponse | None = None,
|
| 206 |
+
responder_id: tuple[x509.Certificate, OCSPResponderEncoding]
|
| 207 |
+
| None = None,
|
| 208 |
+
certs: list[x509.Certificate] | None = None,
|
| 209 |
+
extensions: list[x509.Extension[x509.ExtensionType]] = [],
|
| 210 |
+
):
|
| 211 |
+
self._response = response
|
| 212 |
+
self._responder_id = responder_id
|
| 213 |
+
self._certs = certs
|
| 214 |
+
self._extensions = extensions
|
| 215 |
+
|
| 216 |
+
def add_response(
|
| 217 |
+
self,
|
| 218 |
+
cert: x509.Certificate,
|
| 219 |
+
issuer: x509.Certificate,
|
| 220 |
+
algorithm: hashes.HashAlgorithm,
|
| 221 |
+
cert_status: OCSPCertStatus,
|
| 222 |
+
this_update: datetime.datetime,
|
| 223 |
+
next_update: datetime.datetime | None,
|
| 224 |
+
revocation_time: datetime.datetime | None,
|
| 225 |
+
revocation_reason: x509.ReasonFlags | None,
|
| 226 |
+
) -> OCSPResponseBuilder:
|
| 227 |
+
if self._response is not None:
|
| 228 |
+
raise ValueError("Only one response per OCSPResponse.")
|
| 229 |
+
|
| 230 |
+
if not isinstance(cert, x509.Certificate) or not isinstance(
|
| 231 |
+
issuer, x509.Certificate
|
| 232 |
+
):
|
| 233 |
+
raise TypeError("cert and issuer must be a Certificate")
|
| 234 |
+
|
| 235 |
+
singleresp = _SingleResponse(
|
| 236 |
+
(cert, issuer),
|
| 237 |
+
None,
|
| 238 |
+
algorithm,
|
| 239 |
+
cert_status,
|
| 240 |
+
this_update,
|
| 241 |
+
next_update,
|
| 242 |
+
revocation_time,
|
| 243 |
+
revocation_reason,
|
| 244 |
+
)
|
| 245 |
+
return OCSPResponseBuilder(
|
| 246 |
+
singleresp,
|
| 247 |
+
self._responder_id,
|
| 248 |
+
self._certs,
|
| 249 |
+
self._extensions,
|
| 250 |
+
)
|
| 251 |
+
|
| 252 |
+
def add_response_by_hash(
|
| 253 |
+
self,
|
| 254 |
+
issuer_name_hash: bytes,
|
| 255 |
+
issuer_key_hash: bytes,
|
| 256 |
+
serial_number: int,
|
| 257 |
+
algorithm: hashes.HashAlgorithm,
|
| 258 |
+
cert_status: OCSPCertStatus,
|
| 259 |
+
this_update: datetime.datetime,
|
| 260 |
+
next_update: datetime.datetime | None,
|
| 261 |
+
revocation_time: datetime.datetime | None,
|
| 262 |
+
revocation_reason: x509.ReasonFlags | None,
|
| 263 |
+
) -> OCSPResponseBuilder:
|
| 264 |
+
if self._response is not None:
|
| 265 |
+
raise ValueError("Only one response per OCSPResponse.")
|
| 266 |
+
|
| 267 |
+
if not isinstance(serial_number, int):
|
| 268 |
+
raise TypeError("serial_number must be an integer")
|
| 269 |
+
|
| 270 |
+
utils._check_bytes("issuer_name_hash", issuer_name_hash)
|
| 271 |
+
utils._check_bytes("issuer_key_hash", issuer_key_hash)
|
| 272 |
+
_verify_algorithm(algorithm)
|
| 273 |
+
if algorithm.digest_size != len(
|
| 274 |
+
issuer_name_hash
|
| 275 |
+
) or algorithm.digest_size != len(issuer_key_hash):
|
| 276 |
+
raise ValueError(
|
| 277 |
+
"issuer_name_hash and issuer_key_hash must be the same length "
|
| 278 |
+
"as the digest size of the algorithm"
|
| 279 |
+
)
|
| 280 |
+
|
| 281 |
+
singleresp = _SingleResponse(
|
| 282 |
+
None,
|
| 283 |
+
(issuer_name_hash, issuer_key_hash, serial_number),
|
| 284 |
+
algorithm,
|
| 285 |
+
cert_status,
|
| 286 |
+
this_update,
|
| 287 |
+
next_update,
|
| 288 |
+
revocation_time,
|
| 289 |
+
revocation_reason,
|
| 290 |
+
)
|
| 291 |
+
return OCSPResponseBuilder(
|
| 292 |
+
singleresp,
|
| 293 |
+
self._responder_id,
|
| 294 |
+
self._certs,
|
| 295 |
+
self._extensions,
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
def responder_id(
|
| 299 |
+
self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate
|
| 300 |
+
) -> OCSPResponseBuilder:
|
| 301 |
+
if self._responder_id is not None:
|
| 302 |
+
raise ValueError("responder_id can only be set once")
|
| 303 |
+
if not isinstance(responder_cert, x509.Certificate):
|
| 304 |
+
raise TypeError("responder_cert must be a Certificate")
|
| 305 |
+
if not isinstance(encoding, OCSPResponderEncoding):
|
| 306 |
+
raise TypeError(
|
| 307 |
+
"encoding must be an element from OCSPResponderEncoding"
|
| 308 |
+
)
|
| 309 |
+
|
| 310 |
+
return OCSPResponseBuilder(
|
| 311 |
+
self._response,
|
| 312 |
+
(responder_cert, encoding),
|
| 313 |
+
self._certs,
|
| 314 |
+
self._extensions,
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
def certificates(
|
| 318 |
+
self, certs: Iterable[x509.Certificate]
|
| 319 |
+
) -> OCSPResponseBuilder:
|
| 320 |
+
if self._certs is not None:
|
| 321 |
+
raise ValueError("certificates may only be set once")
|
| 322 |
+
certs = list(certs)
|
| 323 |
+
if len(certs) == 0:
|
| 324 |
+
raise ValueError("certs must not be an empty list")
|
| 325 |
+
if not all(isinstance(x, x509.Certificate) for x in certs):
|
| 326 |
+
raise TypeError("certs must be a list of Certificates")
|
| 327 |
+
return OCSPResponseBuilder(
|
| 328 |
+
self._response,
|
| 329 |
+
self._responder_id,
|
| 330 |
+
certs,
|
| 331 |
+
self._extensions,
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
def add_extension(
|
| 335 |
+
self, extval: x509.ExtensionType, critical: bool
|
| 336 |
+
) -> OCSPResponseBuilder:
|
| 337 |
+
if not isinstance(extval, x509.ExtensionType):
|
| 338 |
+
raise TypeError("extension must be an ExtensionType")
|
| 339 |
+
|
| 340 |
+
extension = x509.Extension(extval.oid, critical, extval)
|
| 341 |
+
_reject_duplicate_extension(extension, self._extensions)
|
| 342 |
+
|
| 343 |
+
return OCSPResponseBuilder(
|
| 344 |
+
self._response,
|
| 345 |
+
self._responder_id,
|
| 346 |
+
self._certs,
|
| 347 |
+
[*self._extensions, extension],
|
| 348 |
+
)
|
| 349 |
+
|
| 350 |
+
def sign(
|
| 351 |
+
self,
|
| 352 |
+
private_key: CertificateIssuerPrivateKeyTypes,
|
| 353 |
+
algorithm: hashes.HashAlgorithm | None,
|
| 354 |
+
) -> OCSPResponse:
|
| 355 |
+
if self._response is None:
|
| 356 |
+
raise ValueError("You must add a response before signing")
|
| 357 |
+
if self._responder_id is None:
|
| 358 |
+
raise ValueError("You must add a responder_id before signing")
|
| 359 |
+
|
| 360 |
+
return ocsp.create_ocsp_response(
|
| 361 |
+
OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm
|
| 362 |
+
)
|
| 363 |
+
|
| 364 |
+
@classmethod
|
| 365 |
+
def build_unsuccessful(
|
| 366 |
+
cls, response_status: OCSPResponseStatus
|
| 367 |
+
) -> OCSPResponse:
|
| 368 |
+
if not isinstance(response_status, OCSPResponseStatus):
|
| 369 |
+
raise TypeError(
|
| 370 |
+
"response_status must be an item from OCSPResponseStatus"
|
| 371 |
+
)
|
| 372 |
+
if response_status is OCSPResponseStatus.SUCCESSFUL:
|
| 373 |
+
raise ValueError("response_status cannot be SUCCESSFUL")
|
| 374 |
+
|
| 375 |
+
return ocsp.create_ocsp_response(response_status, None, None, None)
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
load_der_ocsp_request = ocsp.load_der_ocsp_request
|
| 379 |
+
load_der_ocsp_response = ocsp.load_der_ocsp_response
|
.mcp-venv/lib/python3.13/site-packages/cryptography/x509/oid.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.hazmat._oid import (
|
| 8 |
+
AttributeOID,
|
| 9 |
+
AuthorityInformationAccessOID,
|
| 10 |
+
CertificatePoliciesOID,
|
| 11 |
+
CRLEntryExtensionOID,
|
| 12 |
+
ExtendedKeyUsageOID,
|
| 13 |
+
ExtensionOID,
|
| 14 |
+
NameOID,
|
| 15 |
+
ObjectIdentifier,
|
| 16 |
+
OCSPExtensionOID,
|
| 17 |
+
OtherNameFormOID,
|
| 18 |
+
PublicKeyAlgorithmOID,
|
| 19 |
+
SignatureAlgorithmOID,
|
| 20 |
+
SubjectInformationAccessOID,
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
__all__ = [
|
| 24 |
+
"AttributeOID",
|
| 25 |
+
"AuthorityInformationAccessOID",
|
| 26 |
+
"CRLEntryExtensionOID",
|
| 27 |
+
"CertificatePoliciesOID",
|
| 28 |
+
"ExtendedKeyUsageOID",
|
| 29 |
+
"ExtensionOID",
|
| 30 |
+
"NameOID",
|
| 31 |
+
"OCSPExtensionOID",
|
| 32 |
+
"ObjectIdentifier",
|
| 33 |
+
"OtherNameFormOID",
|
| 34 |
+
"PublicKeyAlgorithmOID",
|
| 35 |
+
"SignatureAlgorithmOID",
|
| 36 |
+
"SubjectInformationAccessOID",
|
| 37 |
+
]
|
.mcp-venv/lib/python3.13/site-packages/cryptography/x509/verification.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import typing
|
| 8 |
+
|
| 9 |
+
from cryptography.hazmat.bindings._rust import x509 as rust_x509
|
| 10 |
+
from cryptography.x509.general_name import DNSName, IPAddress
|
| 11 |
+
|
| 12 |
+
__all__ = [
|
| 13 |
+
"ClientVerifier",
|
| 14 |
+
"Criticality",
|
| 15 |
+
"ExtensionPolicy",
|
| 16 |
+
"Policy",
|
| 17 |
+
"PolicyBuilder",
|
| 18 |
+
"ServerVerifier",
|
| 19 |
+
"Store",
|
| 20 |
+
"Subject",
|
| 21 |
+
"VerificationError",
|
| 22 |
+
"VerifiedClient",
|
| 23 |
+
]
|
| 24 |
+
|
| 25 |
+
Store = rust_x509.Store
|
| 26 |
+
Subject = typing.Union[DNSName, IPAddress]
|
| 27 |
+
VerifiedClient = rust_x509.VerifiedClient
|
| 28 |
+
ClientVerifier = rust_x509.ClientVerifier
|
| 29 |
+
ServerVerifier = rust_x509.ServerVerifier
|
| 30 |
+
PolicyBuilder = rust_x509.PolicyBuilder
|
| 31 |
+
Policy = rust_x509.Policy
|
| 32 |
+
ExtensionPolicy = rust_x509.ExtensionPolicy
|
| 33 |
+
Criticality = rust_x509.Criticality
|
| 34 |
+
VerificationError = rust_x509.VerificationError
|
.mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
.mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: h11
|
| 3 |
+
Version: 0.16.0
|
| 4 |
+
Summary: A pure-Python, bring-your-own-I/O implementation of HTTP/1.1
|
| 5 |
+
Home-page: https://github.com/python-hyper/h11
|
| 6 |
+
Author: Nathaniel J. Smith
|
| 7 |
+
Author-email: njs@pobox.com
|
| 8 |
+
License: MIT
|
| 9 |
+
Classifier: Development Status :: 3 - Alpha
|
| 10 |
+
Classifier: Intended Audience :: Developers
|
| 11 |
+
Classifier: License :: OSI Approved :: MIT License
|
| 12 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 13 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 14 |
+
Classifier: Programming Language :: Python :: 3
|
| 15 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 21 |
+
Classifier: Topic :: Internet :: WWW/HTTP
|
| 22 |
+
Classifier: Topic :: System :: Networking
|
| 23 |
+
Requires-Python: >=3.8
|
| 24 |
+
License-File: LICENSE.txt
|
| 25 |
+
Dynamic: author
|
| 26 |
+
Dynamic: author-email
|
| 27 |
+
Dynamic: classifier
|
| 28 |
+
Dynamic: description
|
| 29 |
+
Dynamic: home-page
|
| 30 |
+
Dynamic: license
|
| 31 |
+
Dynamic: license-file
|
| 32 |
+
Dynamic: requires-python
|
| 33 |
+
Dynamic: summary
|
| 34 |
+
|
| 35 |
+
h11
|
| 36 |
+
===
|
| 37 |
+
|
| 38 |
+
.. image:: https://travis-ci.org/python-hyper/h11.svg?branch=master
|
| 39 |
+
:target: https://travis-ci.org/python-hyper/h11
|
| 40 |
+
:alt: Automated test status
|
| 41 |
+
|
| 42 |
+
.. image:: https://codecov.io/gh/python-hyper/h11/branch/master/graph/badge.svg
|
| 43 |
+
:target: https://codecov.io/gh/python-hyper/h11
|
| 44 |
+
:alt: Test coverage
|
| 45 |
+
|
| 46 |
+
.. image:: https://readthedocs.org/projects/h11/badge/?version=latest
|
| 47 |
+
:target: http://h11.readthedocs.io/en/latest/?badge=latest
|
| 48 |
+
:alt: Documentation Status
|
| 49 |
+
|
| 50 |
+
This is a little HTTP/1.1 library written from scratch in Python,
|
| 51 |
+
heavily inspired by `hyper-h2 <https://hyper-h2.readthedocs.io/>`_.
|
| 52 |
+
|
| 53 |
+
It's a "bring-your-own-I/O" library; h11 contains no IO code
|
| 54 |
+
whatsoever. This means you can hook h11 up to your favorite network
|
| 55 |
+
API, and that could be anything you want: synchronous, threaded,
|
| 56 |
+
asynchronous, or your own implementation of `RFC 6214
|
| 57 |
+
<https://tools.ietf.org/html/rfc6214>`_ -- h11 won't judge you.
|
| 58 |
+
(Compare this to the current state of the art, where every time a `new
|
| 59 |
+
network API <https://trio.readthedocs.io/>`_ comes along then someone
|
| 60 |
+
gets to start over reimplementing the entire HTTP protocol from
|
| 61 |
+
scratch.) Cory Benfield made an `excellent blog post describing the
|
| 62 |
+
benefits of this approach
|
| 63 |
+
<https://lukasa.co.uk/2015/10/The_New_Hyper/>`_, or if you like video
|
| 64 |
+
then here's his `PyCon 2016 talk on the same theme
|
| 65 |
+
<https://www.youtube.com/watch?v=7cC3_jGwl_U>`_.
|
| 66 |
+
|
| 67 |
+
This also means that h11 is not immediately useful out of the box:
|
| 68 |
+
it's a toolkit for building programs that speak HTTP, not something
|
| 69 |
+
that could directly replace ``requests`` or ``twisted.web`` or
|
| 70 |
+
whatever. But h11 makes it much easier to implement something like
|
| 71 |
+
``requests`` or ``twisted.web``.
|
| 72 |
+
|
| 73 |
+
At a high level, working with h11 goes like this:
|
| 74 |
+
|
| 75 |
+
1) First, create an ``h11.Connection`` object to track the state of a
|
| 76 |
+
single HTTP/1.1 connection.
|
| 77 |
+
|
| 78 |
+
2) When you read data off the network, pass it to
|
| 79 |
+
``conn.receive_data(...)``; you'll get back a list of objects
|
| 80 |
+
representing high-level HTTP "events".
|
| 81 |
+
|
| 82 |
+
3) When you want to send a high-level HTTP event, create the
|
| 83 |
+
corresponding "event" object and pass it to ``conn.send(...)``;
|
| 84 |
+
this will give you back some bytes that you can then push out
|
| 85 |
+
through the network.
|
| 86 |
+
|
| 87 |
+
For example, a client might instantiate and then send a
|
| 88 |
+
``h11.Request`` object, then zero or more ``h11.Data`` objects for the
|
| 89 |
+
request body (e.g., if this is a POST), and then a
|
| 90 |
+
``h11.EndOfMessage`` to indicate the end of the message. Then the
|
| 91 |
+
server would then send back a ``h11.Response``, some ``h11.Data``, and
|
| 92 |
+
its own ``h11.EndOfMessage``. If either side violates the protocol,
|
| 93 |
+
you'll get a ``h11.ProtocolError`` exception.
|
| 94 |
+
|
| 95 |
+
h11 is suitable for implementing both servers and clients, and has a
|
| 96 |
+
pleasantly symmetric API: the events you send as a client are exactly
|
| 97 |
+
the ones that you receive as a server and vice-versa.
|
| 98 |
+
|
| 99 |
+
`Here's an example of a tiny HTTP client
|
| 100 |
+
<https://github.com/python-hyper/h11/blob/master/examples/basic-client.py>`_
|
| 101 |
+
|
| 102 |
+
It also has `a fine manual <https://h11.readthedocs.io/>`_.
|
| 103 |
+
|
| 104 |
+
FAQ
|
| 105 |
+
---
|
| 106 |
+
|
| 107 |
+
*Whyyyyy?*
|
| 108 |
+
|
| 109 |
+
I wanted to play with HTTP in `Curio
|
| 110 |
+
<https://curio.readthedocs.io/en/latest/tutorial.html>`__ and `Trio
|
| 111 |
+
<https://trio.readthedocs.io>`__, which at the time didn't have any
|
| 112 |
+
HTTP libraries. So I thought, no big deal, Python has, like, a dozen
|
| 113 |
+
different implementations of HTTP, surely I can find one that's
|
| 114 |
+
reusable. I didn't find one, but I did find Cory's call-to-arms
|
| 115 |
+
blog-post. So I figured, well, fine, if I have to implement HTTP from
|
| 116 |
+
scratch, at least I can make sure no-one *else* has to ever again.
|
| 117 |
+
|
| 118 |
+
*Should I use it?*
|
| 119 |
+
|
| 120 |
+
Maybe. You should be aware that it's a very young project. But, it's
|
| 121 |
+
feature complete and has an exhaustive test-suite and complete docs,
|
| 122 |
+
so the next step is for people to try using it and see how it goes
|
| 123 |
+
:-). If you do then please let us know -- if nothing else we'll want
|
| 124 |
+
to talk to you before making any incompatible changes!
|
| 125 |
+
|
| 126 |
+
*What are the features/limitations?*
|
| 127 |
+
|
| 128 |
+
Roughly speaking, it's trying to be a robust, complete, and non-hacky
|
| 129 |
+
implementation of the first "chapter" of the HTTP/1.1 spec: `RFC 7230:
|
| 130 |
+
HTTP/1.1 Message Syntax and Routing
|
| 131 |
+
<https://tools.ietf.org/html/rfc7230>`_. That is, it mostly focuses on
|
| 132 |
+
implementing HTTP at the level of taking bytes on and off the wire,
|
| 133 |
+
and the headers related to that, and tries to be anal about spec
|
| 134 |
+
conformance. It doesn't know about higher-level concerns like URL
|
| 135 |
+
routing, conditional GETs, cross-origin cookie policies, or content
|
| 136 |
+
negotiation. But it does know how to take care of framing,
|
| 137 |
+
cross-version differences in keep-alive handling, and the "obsolete
|
| 138 |
+
line folding" rule, so you can focus your energies on the hard /
|
| 139 |
+
interesting parts for your application, and it tries to support the
|
| 140 |
+
full specification in the sense that any useful HTTP/1.1 conformant
|
| 141 |
+
application should be able to use h11.
|
| 142 |
+
|
| 143 |
+
It's pure Python, and has no dependencies outside of the standard
|
| 144 |
+
library.
|
| 145 |
+
|
| 146 |
+
It has a test suite with 100.0% coverage for both statements and
|
| 147 |
+
branches.
|
| 148 |
+
|
| 149 |
+
Currently it supports Python 3 (testing on 3.8-3.12) and PyPy 3.
|
| 150 |
+
The last Python 2-compatible version was h11 0.11.x.
|
| 151 |
+
(Originally it had a Cython wrapper for `http-parser
|
| 152 |
+
<https://github.com/nodejs/http-parser>`_ and a beautiful nested state
|
| 153 |
+
machine implemented with ``yield from`` to postprocess the output. But
|
| 154 |
+
I had to take these out -- the new *parser* needs fewer lines-of-code
|
| 155 |
+
than the old *parser wrapper*, is written in pure Python, uses no
|
| 156 |
+
exotic language syntax, and has more features. It's sad, really; that
|
| 157 |
+
old state machine was really slick. I just need a few sentences here
|
| 158 |
+
to mourn that.)
|
| 159 |
+
|
| 160 |
+
I don't know how fast it is. I haven't benchmarked or profiled it yet,
|
| 161 |
+
so it's probably got a few pointless hot spots, and I've been trying
|
| 162 |
+
to err on the side of simplicity and robustness instead of
|
| 163 |
+
micro-optimization. But at the architectural level I tried hard to
|
| 164 |
+
avoid fundamentally bad decisions, e.g., I believe that all the
|
| 165 |
+
parsing algorithms remain linear-time even in the face of pathological
|
| 166 |
+
input like slowloris, and there are no byte-by-byte loops. (I also
|
| 167 |
+
believe that it maintains bounded memory usage in the face of
|
| 168 |
+
arbitrary/pathological input.)
|
| 169 |
+
|
| 170 |
+
The whole library is ~800 lines-of-code. You can read and understand
|
| 171 |
+
the whole thing in less than an hour. Most of the energy invested in
|
| 172 |
+
this so far has been spent on trying to keep things simple by
|
| 173 |
+
minimizing special-cases and ad hoc state manipulation; even though it
|
| 174 |
+
is now quite small and simple, I'm still annoyed that I haven't
|
| 175 |
+
figured out how to make it even smaller and simpler. (Unfortunately,
|
| 176 |
+
HTTP does not lend itself to simplicity.)
|
| 177 |
+
|
| 178 |
+
The API is ~feature complete and I don't expect the general outlines
|
| 179 |
+
to change much, but you can't judge an API's ergonomics until you
|
| 180 |
+
actually document and use it, so I'd expect some changes in the
|
| 181 |
+
details.
|
| 182 |
+
|
| 183 |
+
*How do I try it?*
|
| 184 |
+
|
| 185 |
+
.. code-block:: sh
|
| 186 |
+
|
| 187 |
+
$ pip install h11
|
| 188 |
+
$ git clone git@github.com:python-hyper/h11
|
| 189 |
+
$ cd h11/examples
|
| 190 |
+
$ python basic-client.py
|
| 191 |
+
|
| 192 |
+
and go from there.
|
| 193 |
+
|
| 194 |
+
*License?*
|
| 195 |
+
|
| 196 |
+
MIT
|
| 197 |
+
|
| 198 |
+
*Code of conduct?*
|
| 199 |
+
|
| 200 |
+
Contributors are requested to follow our `code of conduct
|
| 201 |
+
<https://github.com/python-hyper/h11/blob/master/CODE_OF_CONDUCT.md>`_ in
|
| 202 |
+
all project spaces.
|
.mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/RECORD
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
h11-0.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 2 |
+
h11-0.16.0.dist-info/METADATA,sha256=KPMmCYrAn8unm48YD5YIfIQf4kViFct7hyqcfVzRnWQ,8348
|
| 3 |
+
h11-0.16.0.dist-info/RECORD,,
|
| 4 |
+
h11-0.16.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
| 5 |
+
h11-0.16.0.dist-info/licenses/LICENSE.txt,sha256=N9tbuFkm2yikJ6JYZ_ELEjIAOuob5pzLhRE4rbjm82E,1124
|
| 6 |
+
h11-0.16.0.dist-info/top_level.txt,sha256=F7dC4jl3zeh8TGHEPaWJrMbeuoWbS379Gwdi-Yvdcis,4
|
| 7 |
+
h11/__init__.py,sha256=iO1KzkSO42yZ6ffg-VMgbx_ZVTWGUY00nRYEWn-s3kY,1507
|
| 8 |
+
h11/__pycache__/__init__.cpython-313.pyc,,
|
| 9 |
+
h11/__pycache__/_abnf.cpython-313.pyc,,
|
| 10 |
+
h11/__pycache__/_connection.cpython-313.pyc,,
|
| 11 |
+
h11/__pycache__/_events.cpython-313.pyc,,
|
| 12 |
+
h11/__pycache__/_headers.cpython-313.pyc,,
|
| 13 |
+
h11/__pycache__/_readers.cpython-313.pyc,,
|
| 14 |
+
h11/__pycache__/_receivebuffer.cpython-313.pyc,,
|
| 15 |
+
h11/__pycache__/_state.cpython-313.pyc,,
|
| 16 |
+
h11/__pycache__/_util.cpython-313.pyc,,
|
| 17 |
+
h11/__pycache__/_version.cpython-313.pyc,,
|
| 18 |
+
h11/__pycache__/_writers.cpython-313.pyc,,
|
| 19 |
+
h11/_abnf.py,sha256=ybixr0xsupnkA6GFAyMubuXF6Tc1lb_hF890NgCsfNc,4815
|
| 20 |
+
h11/_connection.py,sha256=k9YRVf6koZqbttBW36xSWaJpWdZwa-xQVU9AHEo9DuI,26863
|
| 21 |
+
h11/_events.py,sha256=I97aXoal1Wu7dkL548BANBUCkOIbe-x5CioYA9IBY14,11792
|
| 22 |
+
h11/_headers.py,sha256=P7D-lBNxHwdLZPLimmYwrPG-9ZkjElvvJZJdZAgSP-4,10412
|
| 23 |
+
h11/_readers.py,sha256=a4RypORUCC3d0q_kxPuBIM7jTD8iLt5X91TH0FsduN4,8590
|
| 24 |
+
h11/_receivebuffer.py,sha256=xrspsdsNgWFxRfQcTXxR8RrdjRXXTK0Io5cQYWpJ1Ws,5252
|
| 25 |
+
h11/_state.py,sha256=_5LG_BGR8FCcFQeBPH-TMHgm_-B-EUcWCnQof_9XjFE,13231
|
| 26 |
+
h11/_util.py,sha256=LWkkjXyJaFlAy6Lt39w73UStklFT5ovcvo0TkY7RYuk,4888
|
| 27 |
+
h11/_version.py,sha256=GVSsbPSPDcOuF6ptfIiXnVJoaEm3ygXbMnqlr_Giahw,686
|
| 28 |
+
h11/_writers.py,sha256=oFKm6PtjeHfbj4RLX7VB7KDc1gIY53gXG3_HR9ltmTA,5081
|
| 29 |
+
h11/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7
|
.mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (78.1.0)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
| 5 |
+
|
.mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
The MIT License (MIT)
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2016 Nathaniel J. Smith <njs@pobox.com> and other contributors
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining
|
| 6 |
+
a copy of this software and associated documentation files (the
|
| 7 |
+
"Software"), to deal in the Software without restriction, including
|
| 8 |
+
without limitation the rights to use, copy, modify, merge, publish,
|
| 9 |
+
distribute, sublicense, and/or sell copies of the Software, and to
|
| 10 |
+
permit persons to whom the Software is furnished to do so, subject to
|
| 11 |
+
the following conditions:
|
| 12 |
+
|
| 13 |
+
The above copyright notice and this permission notice shall be
|
| 14 |
+
included in all copies or substantial portions of the Software.
|
| 15 |
+
|
| 16 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
| 17 |
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
| 18 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
| 19 |
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
| 20 |
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
| 21 |
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
| 22 |
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
.mcp-venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
h11
|
.mcp-venv/lib/python3.13/site-packages/h11/__init__.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# A highish-level implementation of the HTTP/1.1 wire protocol (RFC 7230),
|
| 2 |
+
# containing no networking code at all, loosely modelled on hyper-h2's generic
|
| 3 |
+
# implementation of HTTP/2 (and in particular the h2.connection.H2Connection
|
| 4 |
+
# class). There's still a bunch of subtle details you need to get right if you
|
| 5 |
+
# want to make this actually useful, because it doesn't implement all the
|
| 6 |
+
# semantics to check that what you're asking to write to the wire is sensible,
|
| 7 |
+
# but at least it gets you out of dealing with the wire itself.
|
| 8 |
+
|
| 9 |
+
from h11._connection import Connection, NEED_DATA, PAUSED
|
| 10 |
+
from h11._events import (
|
| 11 |
+
ConnectionClosed,
|
| 12 |
+
Data,
|
| 13 |
+
EndOfMessage,
|
| 14 |
+
Event,
|
| 15 |
+
InformationalResponse,
|
| 16 |
+
Request,
|
| 17 |
+
Response,
|
| 18 |
+
)
|
| 19 |
+
from h11._state import (
|
| 20 |
+
CLIENT,
|
| 21 |
+
CLOSED,
|
| 22 |
+
DONE,
|
| 23 |
+
ERROR,
|
| 24 |
+
IDLE,
|
| 25 |
+
MIGHT_SWITCH_PROTOCOL,
|
| 26 |
+
MUST_CLOSE,
|
| 27 |
+
SEND_BODY,
|
| 28 |
+
SEND_RESPONSE,
|
| 29 |
+
SERVER,
|
| 30 |
+
SWITCHED_PROTOCOL,
|
| 31 |
+
)
|
| 32 |
+
from h11._util import LocalProtocolError, ProtocolError, RemoteProtocolError
|
| 33 |
+
from h11._version import __version__
|
| 34 |
+
|
| 35 |
+
PRODUCT_ID = "python-h11/" + __version__
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
__all__ = (
|
| 39 |
+
"Connection",
|
| 40 |
+
"NEED_DATA",
|
| 41 |
+
"PAUSED",
|
| 42 |
+
"ConnectionClosed",
|
| 43 |
+
"Data",
|
| 44 |
+
"EndOfMessage",
|
| 45 |
+
"Event",
|
| 46 |
+
"InformationalResponse",
|
| 47 |
+
"Request",
|
| 48 |
+
"Response",
|
| 49 |
+
"CLIENT",
|
| 50 |
+
"CLOSED",
|
| 51 |
+
"DONE",
|
| 52 |
+
"ERROR",
|
| 53 |
+
"IDLE",
|
| 54 |
+
"MUST_CLOSE",
|
| 55 |
+
"SEND_BODY",
|
| 56 |
+
"SEND_RESPONSE",
|
| 57 |
+
"SERVER",
|
| 58 |
+
"SWITCHED_PROTOCOL",
|
| 59 |
+
"ProtocolError",
|
| 60 |
+
"LocalProtocolError",
|
| 61 |
+
"RemoteProtocolError",
|
| 62 |
+
)
|
.mcp-venv/lib/python3.13/site-packages/h11/_abnf.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# We use native strings for all the re patterns, to take advantage of string
|
| 2 |
+
# formatting, and then convert to bytestrings when compiling the final re
|
| 3 |
+
# objects.
|
| 4 |
+
|
| 5 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#whitespace
|
| 6 |
+
# OWS = *( SP / HTAB )
|
| 7 |
+
# ; optional whitespace
|
| 8 |
+
OWS = r"[ \t]*"
|
| 9 |
+
|
| 10 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.token.separators
|
| 11 |
+
# token = 1*tchar
|
| 12 |
+
#
|
| 13 |
+
# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
| 14 |
+
# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
| 15 |
+
# / DIGIT / ALPHA
|
| 16 |
+
# ; any VCHAR, except delimiters
|
| 17 |
+
token = r"[-!#$%&'*+.^_`|~0-9a-zA-Z]+"
|
| 18 |
+
|
| 19 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#header.fields
|
| 20 |
+
# field-name = token
|
| 21 |
+
field_name = token
|
| 22 |
+
|
| 23 |
+
# The standard says:
|
| 24 |
+
#
|
| 25 |
+
# field-value = *( field-content / obs-fold )
|
| 26 |
+
# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
| 27 |
+
# field-vchar = VCHAR / obs-text
|
| 28 |
+
# obs-fold = CRLF 1*( SP / HTAB )
|
| 29 |
+
# ; obsolete line folding
|
| 30 |
+
# ; see Section 3.2.4
|
| 31 |
+
#
|
| 32 |
+
# https://tools.ietf.org/html/rfc5234#appendix-B.1
|
| 33 |
+
#
|
| 34 |
+
# VCHAR = %x21-7E
|
| 35 |
+
# ; visible (printing) characters
|
| 36 |
+
#
|
| 37 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.quoted-string
|
| 38 |
+
# obs-text = %x80-FF
|
| 39 |
+
#
|
| 40 |
+
# However, the standard definition of field-content is WRONG! It disallows
|
| 41 |
+
# fields containing a single visible character surrounded by whitespace,
|
| 42 |
+
# e.g. "foo a bar".
|
| 43 |
+
#
|
| 44 |
+
# See: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189
|
| 45 |
+
#
|
| 46 |
+
# So our definition of field_content attempts to fix it up...
|
| 47 |
+
#
|
| 48 |
+
# Also, we allow lots of control characters, because apparently people assume
|
| 49 |
+
# that they're legal in practice (e.g., google analytics makes cookies with
|
| 50 |
+
# \x01 in them!):
|
| 51 |
+
# https://github.com/python-hyper/h11/issues/57
|
| 52 |
+
# We still don't allow NUL or whitespace, because those are often treated as
|
| 53 |
+
# meta-characters and letting them through can lead to nasty issues like SSRF.
|
| 54 |
+
vchar = r"[\x21-\x7e]"
|
| 55 |
+
vchar_or_obs_text = r"[^\x00\s]"
|
| 56 |
+
field_vchar = vchar_or_obs_text
|
| 57 |
+
field_content = r"{field_vchar}+(?:[ \t]+{field_vchar}+)*".format(**globals())
|
| 58 |
+
|
| 59 |
+
# We handle obs-fold at a different level, and our fixed-up field_content
|
| 60 |
+
# already grows to swallow the whole value, so ? instead of *
|
| 61 |
+
field_value = r"({field_content})?".format(**globals())
|
| 62 |
+
|
| 63 |
+
# header-field = field-name ":" OWS field-value OWS
|
| 64 |
+
header_field = (
|
| 65 |
+
r"(?P<field_name>{field_name})"
|
| 66 |
+
r":"
|
| 67 |
+
r"{OWS}"
|
| 68 |
+
r"(?P<field_value>{field_value})"
|
| 69 |
+
r"{OWS}".format(**globals())
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#request.line
|
| 73 |
+
#
|
| 74 |
+
# request-line = method SP request-target SP HTTP-version CRLF
|
| 75 |
+
# method = token
|
| 76 |
+
# HTTP-version = HTTP-name "/" DIGIT "." DIGIT
|
| 77 |
+
# HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive
|
| 78 |
+
#
|
| 79 |
+
# request-target is complicated (see RFC 7230 sec 5.3) -- could be path, full
|
| 80 |
+
# URL, host+port (for connect), or even "*", but in any case we are guaranteed
|
| 81 |
+
# that it contists of the visible printing characters.
|
| 82 |
+
method = token
|
| 83 |
+
request_target = r"{vchar}+".format(**globals())
|
| 84 |
+
http_version = r"HTTP/(?P<http_version>[0-9]\.[0-9])"
|
| 85 |
+
request_line = (
|
| 86 |
+
r"(?P<method>{method})"
|
| 87 |
+
r" "
|
| 88 |
+
r"(?P<target>{request_target})"
|
| 89 |
+
r" "
|
| 90 |
+
r"{http_version}".format(**globals())
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#status.line
|
| 94 |
+
#
|
| 95 |
+
# status-line = HTTP-version SP status-code SP reason-phrase CRLF
|
| 96 |
+
# status-code = 3DIGIT
|
| 97 |
+
# reason-phrase = *( HTAB / SP / VCHAR / obs-text )
|
| 98 |
+
status_code = r"[0-9]{3}"
|
| 99 |
+
reason_phrase = r"([ \t]|{vchar_or_obs_text})*".format(**globals())
|
| 100 |
+
status_line = (
|
| 101 |
+
r"{http_version}"
|
| 102 |
+
r" "
|
| 103 |
+
r"(?P<status_code>{status_code})"
|
| 104 |
+
# However, there are apparently a few too many servers out there that just
|
| 105 |
+
# leave out the reason phrase:
|
| 106 |
+
# https://github.com/scrapy/scrapy/issues/345#issuecomment-281756036
|
| 107 |
+
# https://github.com/seanmonstar/httparse/issues/29
|
| 108 |
+
# so make it optional. ?: is a non-capturing group.
|
| 109 |
+
r"(?: (?P<reason>{reason_phrase}))?".format(**globals())
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
HEXDIG = r"[0-9A-Fa-f]"
|
| 113 |
+
# Actually
|
| 114 |
+
#
|
| 115 |
+
# chunk-size = 1*HEXDIG
|
| 116 |
+
#
|
| 117 |
+
# but we impose an upper-limit to avoid ridiculosity. len(str(2**64)) == 20
|
| 118 |
+
chunk_size = r"({HEXDIG}){{1,20}}".format(**globals())
|
| 119 |
+
# Actually
|
| 120 |
+
#
|
| 121 |
+
# chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
| 122 |
+
#
|
| 123 |
+
# but we aren't parsing the things so we don't really care.
|
| 124 |
+
chunk_ext = r";.*"
|
| 125 |
+
chunk_header = (
|
| 126 |
+
r"(?P<chunk_size>{chunk_size})"
|
| 127 |
+
r"(?P<chunk_ext>{chunk_ext})?"
|
| 128 |
+
r"{OWS}\r\n".format(
|
| 129 |
+
**globals()
|
| 130 |
+
) # Even though the specification does not allow for extra whitespaces,
|
| 131 |
+
# we are lenient with trailing whitespaces because some servers on the wild use it.
|
| 132 |
+
)
|