Jelajahi Sumber

a lot of changes: jwt, add classes, mailing list and other

WorldOfPets 2 tahun lalu
induk
melakukan
898e63f9e0
65 mengubah file dengan 9073 tambahan dan 80 penghapusan
  1. 22 7
      README.md
  2. TEMPAT SAMPAH
      __pycache__/config.cpython-39.pyc
  3. TEMPAT SAMPAH
      __pycache__/function.cpython-39.pyc
  4. 67 33
      api.py
  5. 4 1
      config.py
  6. 47 38
      function.py
  7. 1 0
      rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/INSTALLER
  8. 21 0
      rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/LICENSE
  9. 113 0
      rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/METADATA
  10. 28 0
      rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/RECORD
  11. 0 0
      rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/REQUESTED
  12. 6 0
      rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/WHEEL
  13. 1 0
      rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/top_level.txt
  14. 2634 0
      rest_venv/Lib/site-packages/OpenSSL/SSL.py
  15. 32 0
      rest_venv/Lib/site-packages/OpenSSL/__init__.py
  16. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/OpenSSL/__pycache__/SSL.cpython-39.pyc
  17. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/OpenSSL/__pycache__/__init__.cpython-39.pyc
  18. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/OpenSSL/__pycache__/_util.cpython-39.pyc
  19. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/OpenSSL/__pycache__/crypto.cpython-39.pyc
  20. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/OpenSSL/__pycache__/debug.cpython-39.pyc
  21. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/OpenSSL/__pycache__/rand.cpython-39.pyc
  22. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/OpenSSL/__pycache__/version.cpython-39.pyc
  23. 122 0
      rest_venv/Lib/site-packages/OpenSSL/_util.py
  24. 3277 0
      rest_venv/Lib/site-packages/OpenSSL/crypto.py
  25. 42 0
      rest_venv/Lib/site-packages/OpenSSL/debug.py
  26. 40 0
      rest_venv/Lib/site-packages/OpenSSL/rand.py
  27. 28 0
      rest_venv/Lib/site-packages/OpenSSL/version.py
  28. 0 1
      rest_venv/Lib/site-packages/PyJWT-2.4.0.dist-info/RECORD
  29. 22 0
      rest_venv/Lib/site-packages/flask_jwt_extended/__init__.py
  30. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/__init__.cpython-39.pyc
  31. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/config.cpython-39.pyc
  32. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/default_callbacks.cpython-39.pyc
  33. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/exceptions.cpython-39.pyc
  34. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/internal_utils.cpython-39.pyc
  35. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/jwt_manager.cpython-39.pyc
  36. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/tokens.cpython-39.pyc
  37. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/typing.cpython-39.pyc
  38. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/utils.cpython-39.pyc
  39. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/view_decorators.cpython-39.pyc
  40. 314 0
      rest_venv/Lib/site-packages/flask_jwt_extended/config.py
  41. 161 0
      rest_venv/Lib/site-packages/flask_jwt_extended/default_callbacks.py
  42. 102 0
      rest_venv/Lib/site-packages/flask_jwt_extended/exceptions.py
  43. 50 0
      rest_venv/Lib/site-packages/flask_jwt_extended/internal_utils.py
  44. 548 0
      rest_venv/Lib/site-packages/flask_jwt_extended/jwt_manager.py
  45. 0 0
      rest_venv/Lib/site-packages/flask_jwt_extended/py.typed
  46. 123 0
      rest_venv/Lib/site-packages/flask_jwt_extended/tokens.py
  47. 10 0
      rest_venv/Lib/site-packages/flask_jwt_extended/typing.py
  48. 458 0
      rest_venv/Lib/site-packages/flask_jwt_extended/utils.py
  49. 352 0
      rest_venv/Lib/site-packages/flask_jwt_extended/view_decorators.py
  50. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/jwt/__pycache__/__init__.cpython-39.pyc
  51. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/jwt/__pycache__/algorithms.cpython-39.pyc
  52. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/jwt/__pycache__/api_jwk.cpython-39.pyc
  53. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/jwt/__pycache__/api_jws.cpython-39.pyc
  54. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/jwt/__pycache__/api_jwt.cpython-39.pyc
  55. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/jwt/__pycache__/exceptions.cpython-39.pyc
  56. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/jwt/__pycache__/help.cpython-39.pyc
  57. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/jwt/__pycache__/jwks_client.cpython-39.pyc
  58. TEMPAT SAMPAH
      rest_venv/Lib/site-packages/jwt/__pycache__/utils.cpython-39.pyc
  59. 1 0
      rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/INSTALLER
  60. 202 0
      rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/LICENSE
  61. 217 0
      rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/METADATA
  62. 21 0
      rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/RECORD
  63. 0 0
      rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/REQUESTED
  64. 6 0
      rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/WHEEL
  65. 1 0
      rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/top_level.txt

+ 22 - 7
README.md

@@ -1,7 +1,7 @@
 # REST API Python
 ## Instruction
 1. Change config.py
-2. Open **cmd** and go to ```cd C:/your_path/RestApiPython/rest_api_venv/Scripts```
+2. Open **cmd** and go to ```cd C:/your_path/pythoRestApi/rest_api_venv/Scripts```
 3. Entered the following commands ```activate.bat```, ```cd ../../``` and ```python api.py```
 
 ## Requests
@@ -13,7 +13,9 @@
     "idrole": 2,
     "insys": true,
     "lastlogintime": "Tue, 07 Jun 2022 19:25:00 GMT",
-    "name": "Evgen4"
+    "name": "Evgen4",
+    "email" : "test@test.com",
+    "password" : "1234"
 }
 ```
 ### GET ```http://127.0.0.2:8080/users``` - get all users
@@ -25,7 +27,9 @@
         "insys": true,
         "lastlogintime": "Mon, 16 May 2011 15:36:38 GMT",
         "name": "Evgen Polivanov",
-        "role_name": "Admin"
+        "role_name": "Admin",
+        "email" : "test@test.com",
+        "password" : "1234"
     },
     {
         "birthday": "Tue, 07 Jun 2022 00:00:00 GMT",
@@ -33,7 +37,9 @@
         "insys": true,
         "lastlogintime": "Tue, 07 Jun 2022 16:58:29 GMT",
         "name": "Evgen Polivanov",
-        "role_name": "User"
+        "role_name": "User",
+        "email" : "test@test.com",
+        "password" : "1234"
     }
 ]
 ```
@@ -45,14 +51,23 @@
     "reg_date" : "07-06-2022",
     "log_time" : "07-06-2022 19:25",
     "in_sys" : true,
-    "role_id" : 2
+    "role_id" : 2,
+    "email" : "test@test.com",
+    "password" : "1234"
 }
 ```
-### POST ```http://127.0.0.2:8080/updateuser``` - update name user by id
+### POST ```http://127.0.0.2:8080/auth``` - login in system. Return access_token.
+**BODY**
+```json
+{
+    "login_email" : "Evgen",
+    "password" : "1234"
+}
+```
+### POST ```http://127.0.0.2:8080/updateuser``` - update name user by access_token
 **BODY**
 ```json
 {
-    "id" : 10,
     "name" : "Other name"
 }
 ```

TEMPAT SAMPAH
__pycache__/config.cpython-39.pyc


TEMPAT SAMPAH
__pycache__/function.cpython-39.pyc


+ 67 - 33
api.py

@@ -1,87 +1,121 @@
+from array import array
 from flask import Flask, jsonify, request
 from xmpp import cli
-from function import query_select, query_insert, query_delete, query_update
-import jwt
+from function import MyQueryClass
+from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity
+from datetime import datetime, timedelta
+from config import Config
+#from OpenSSL import SSL
 
+#context = SSL.Context(SSL.PROTOCOL_TLSv1_2)
+#context.use_privatekey_file('server.key')
+#context.use_certificate_file('server.crt')
+### ssl_context=context in run
+myquery = MyQueryClass()
 app = Flask(__name__)
+app.config.from_object(Config)
+jwt1 = JWTManager(app)
 
 @app.route('/auth', methods=['POST'])
 def post_auth():
-    request_data = request.get_json()
-    result = query_select('''
-    SELECT FROM rest_user 
-    WHERE ();
-    ''', ())
+    request_data =request.get_json()
+    loginemail = request_data['login_email']
+    
+    result = myquery.query_select('''
+    SELECT * FROM rest_user
+    WHERE (rest_user.name=%s OR rest_user.email=%s) AND rest_user.password=%s;
+    ''', (loginemail, loginemail, request_data['password'],), True)
+    if(result != False):
+        return gen_token(result['id'], 5), 201
+    else:
+        return jsonify({"msg":"Server error!"}), 500
 
-    return jsonify(result), 201
+def gen_token(id, minut):
+    token = {"access_token" : create_access_token(identity=id, expires_delta=timedelta(minutes=minut))}
+    return jsonify(token)
 
 @app.route('/adduser', methods=['POST'])
 def get_add():
     request_data = request.get_json()
-    result = query_insert('''
-    INSERT INTO rest_user (name, birthday, lastlogintime, insys, idrole) 
-    VALUES (%s, %s, %s, %s, %s)
-    RETURNING id;
-    ''', \
-        (request_data['name'], request_data['reg_date'],\
-             request_data['log_time'], request_data['in_sys'],\
-                  request_data['role_id']))
-
-    return jsonify(result), 201
+    proverka = myquery.query_proverka(''' 
+    SELECT * FROM rest_user
+    WHERE rest_user.name=%s OR rest_user.email=%s;
+    ''', (request_data['name'], request_data['email']))
+    if(proverka):
+        return jsonify({'msg':"User exist!"})
+    else:
+        result = myquery.query_insert('''
+        INSERT INTO rest_user (name, birthday, lastlogintime, insys, idrole, email, password) 
+        VALUES (%s, %s, %s, %s, %s, %s, %s)
+        RETURNING id;
+        ''', \
+            (request_data['name'], request_data['reg_date'],\
+                request_data['log_time'], request_data['in_sys'],\
+                    request_data['role_id'], request_data['email'], request_data['password']))
+        if(result != False):
+            return jsonify(result), 201
+        else:
+            return jsonify({"msg":"Server error!"}), 500
 
 @app.route('/user/<int:id>', methods=['GET'])
 def get_user(id):
-    return jsonify(query_select('''
+    return jsonify(myquery.query_select('''
     SELECT * FROM rest_user WHERE id = %s;
     ''', (id,), True)), 200
 
 @app.route('/users', methods=['GET'])
+#@jwt_required()
 def get_show_user():
-    result = query_select('''
-    SELECT rest_user.id, rest_user.name, rest_user.birthday, rest_user.insys, rest_role.name AS role_name, rest_user.lastlogintime 
+    result = myquery.query_select('''
+    SELECT rest_user.id, rest_user.name, rest_user.birthday, rest_user.insys, rest_role.name AS role_name, rest_user.lastlogintime, rest_user.email, rest_user.password
     FROM rest_user, rest_role
     WHERE rest_user.idrole = rest_role.id
     ORDER BY id ASC;
     ''', (), False)
-    return jsonify(result), 200
+    if(result != False):
+        return jsonify(result), 200
+    else:
+        return jsonify({"msg":"Server error!"}), 500
 
 @app.route('/deleteuser', methods=['POST'])
 def delete_user():
     request_data = request.get_json()
-    result = query_delete('''
+    result = myquery.query_delete('''
     DELETE FROM rest_user
     WHERE id = %s;
     ''', (request_data['id'],))
     if(result):
-        return "Успешное удаление!", 200
+        return jsonify({"msg":"Success delete!"}), 200
     else:
-        return "Ошибка", 500
+        return jsonify({"msg":"Server error!"}), 500
 
 @app.route('/updateuser', methods=['POST'])
+@jwt_required()
 def test_api():
     try:
         request_data = request.get_json()
-        result = query_update(f'''
+        result = myquery.query_update(f'''
         UPDATE rest_user
         SET name = %s
         WHERE id = %s;
-        ''', (request_data['name'], request_data['id']))
+        ''', (request_data['name'], get_jwt_identity()))
         if(result):
-            return "Успешное изменение!", 200
+            return jsonify({"msg":"Success update!"}), 200
         else:
-            return "Ошибка", 400
+            return jsonify({"msg":"User not found!"}), 400
     except:
-        return "Ошибка", 500
+        return jsonify({"msg":"Server error!"}), 500
 
 
 @app.route('/testmessage', methods=['POST'])
 def send_mess():
     try:
         request_data = request.get_json()
-        cli.send_message(request_data['from'], request_data['password'], request_data['to'], request_data['message'])
-        return "Успешное изменение!", 200
+        for to in request_data['to']:
+            cli.send_message(request_data['from'], request_data['password'], to, request_data['message'])
+        return jsonify({"msg":"Succes send!"}), 200
     except:
-        return "Ошибка", 500
+        return jsonify({"msg":"Server error!"}), 500
 
 
 if __name__ == '__main__':

+ 4 - 1
config.py

@@ -4,4 +4,7 @@ user = "postgres"
 password = "password"
 db_name = "postgres"
 port = 3306
-SSH_H = "46.138.247.90"
+SSH_H = "46.138.247.90"
+
+class Config:
+    SECRET_KEY = "2198jf1239198234790182u3f9"

+ 47 - 38
function.py

@@ -1,45 +1,54 @@
 from connect import connect
 
 conn = connect()
+class MyQueryClass:
+    def query_select(self, query, args=(), one=False):
+        try:
+            with conn.cursor() as cursor:
+                cursor.execute(query, args)
+                r = [dict((cursor.description[i][0], value) \
+                        for i, value in enumerate(row)) for row in cursor.fetchall()]
+                return (r[0] if r else None) if one else r
+        except:
+            return False
 
-def query_select(query, args=(), one=False):
-    try:
-        with conn.cursor() as cursor:
-            cursor.execute(query, args)
-            r = [dict((cursor.description[i][0], value) \
-                    for i, value in enumerate(row)) for row in cursor.fetchall()]
-            return (r[0] if r else None) if one else r
-    except:
-        return "Error"
+    def query_insert(self, query, args=()):
+        try:
+            with conn.cursor() as cursor:
+                cursor.execute(query, args)
+                conn.commit()
+                return cursor.fetchone()[0]
+        except: 
+            return False
 
-def query_insert(query, args=()):
-    try:
-        with conn.cursor() as cursor:
-            cursor.execute(query, args)
-            conn.commit()
-            return cursor.fetchone()[0]
-    except: 
-        return "Error"
+    def query_delete(self, query, args=()):
+        try:
+            with conn.cursor() as cursor:
+                cursor.execute(query, args)
+                if(cursor.rowcount != 0):
+                    conn.commit()
+                    return True
+            return False
+        except: 
+            return False
 
-def query_delete(query, args=()):
-    try:
-        with conn.cursor() as cursor:
-            cursor.execute(query, args)
-            if(cursor.rowcount != 0):
-                conn.commit()
-                return True
-        return False
-    except: 
-        return False
+    def query_update(self, query, args=()):
+        try:
+            with conn.cursor() as cursor:
+                cursor.execute(query, args)
+                if(cursor.rowcount != 0):
+                    conn.commit()
+                    return True
+            return False
+        except: 
+            return False
 
-def query_update(query, args=()):
-    try:
-        with conn.cursor() as cursor:
-            cursor.execute(query, args)
-            print(cursor.rowcount)
-            if(cursor.rowcount != 0):
-                conn.commit()
-                return True
-        return False
-    except: 
-        return False
+    def query_proverka(self, query, args=()):
+        try:
+            with conn.cursor() as cursor:
+                cursor.execute(query, args)
+                if(cursor.rowcount > 0):
+                    return True
+            return False
+        except: 
+            return False

+ 1 - 0
rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/INSTALLER

@@ -0,0 +1 @@
+pip

+ 21 - 0
rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Lily Acadia Gilbert
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 113 - 0
rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/METADATA

@@ -0,0 +1,113 @@
+Metadata-Version: 2.1
+Name: Flask-JWT-Extended
+Version: 4.4.1
+Summary: Extended JWT integration with Flask
+Home-page: https://github.com/vimalloc/flask-jwt-extended
+Author: Lily Acadia Gilbert
+Author-email: lily.gilbert@hey.com
+License: MIT
+Keywords: flask,jwt,json web token
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Framework :: Flask
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.6,<4
+Description-Content-Type: text/markdown
+License-File: LICENSE
+Requires-Dist: Werkzeug (>=0.14)
+Requires-Dist: Flask (<3.0,>=2.0)
+Requires-Dist: PyJWT (<3.0,>=2.0)
+Requires-Dist: typing-extensions (>=3.7.4) ; python_version < "3.8"
+Provides-Extra: asymmetric_crypto
+Requires-Dist: cryptography (>=3.3.1) ; extra == 'asymmetric_crypto'
+
+# Flask-JWT-Extended
+
+### Features
+
+Flask-JWT-Extended not only adds support for using JSON Web Tokens (JWT) to Flask for protecting routes,
+but also many helpful (and **optional**) features built in to make working with JSON Web Tokens
+easier. These include:
+
+-   Adding custom claims to JSON Web Tokens
+-   Automatic user loading (`current_user`).
+-   Custom claims validation on received tokens
+-   [Refresh tokens](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/)
+-   First class support for fresh tokens for making sensitive changes.
+-   Token revoking/blocklisting
+-   Storing tokens in cookies and CSRF protection
+
+### Usage
+
+[View the documentation online](https://flask-jwt-extended.readthedocs.io/en/stable/)
+
+### Upgrading from 3.x.x to 4.0.0
+
+[View the changes](https://flask-jwt-extended.readthedocs.io/en/stable/v4_upgrade_guide/)
+
+### Changelog
+
+You can view the changelog [here](https://github.com/vimalloc/flask-jwt-extended/releases).
+This project follows [semantic versioning](https://semver.org/).
+
+### Chatting
+
+Come chat with the community or ask questions at https://discord.gg/EJBsbFd
+
+### Contributing
+
+Before making any changes, make sure to install the development requirements
+and setup the git hooks which will automatically lint and format your changes.
+
+```bash
+pip install -r requirements.txt
+pre-commit install
+```
+
+We require 100% code coverage in our unit tests. You can run the tests locally
+with `tox` which ensures that all tests pass, tests provide complete code coverage,
+documentation builds, and style guide are adhered to
+
+```bash
+tox
+```
+
+A subset of checks can also be ran by adding an argument to tox. The available
+arguments are:
+
+-   py36, py37, py38, py39, py310, pypy3
+    -   Run unit tests on the given python version
+-   mypy
+    -   Run mypy type checking
+-   coverage
+    -   Run a code coverage check
+-   docs
+    -   Ensure documentation builds and there are no broken links
+-   style
+    -   Ensure style guide is adhered to
+
+```bash
+tox -e py38
+```
+
+We also require features to be well documented. You can generate a local copy
+of the documentation by going to the `docs` directory and running:
+
+```bash
+make clean && make html && open _build/html/index.html
+```
+
+

+ 28 - 0
rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/RECORD

@@ -0,0 +1,28 @@
+Flask_JWT_Extended-4.4.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+Flask_JWT_Extended-4.4.1.dist-info/LICENSE,sha256=IZ7JSdUnG3ZAFNkV8wskplpL1tTu-ImxNFJM2Zfci5g,1076
+Flask_JWT_Extended-4.4.1.dist-info/METADATA,sha256=HynMw2fvMatSoCDtfRHSqCHQufth2MUIosAxOf1mTZM,3734
+Flask_JWT_Extended-4.4.1.dist-info/RECORD,,
+Flask_JWT_Extended-4.4.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+Flask_JWT_Extended-4.4.1.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
+Flask_JWT_Extended-4.4.1.dist-info/top_level.txt,sha256=ScNvc4cNmrFFSnAe_rENqiMpfhog1M2HkaUaydRBbUU,19
+flask_jwt_extended/__init__.py,sha256=qa84SWfiXBLOy4r_NwbkL8Uzew8Xgmjw_KIRTqz1GFo,774
+flask_jwt_extended/__pycache__/__init__.cpython-39.pyc,,
+flask_jwt_extended/__pycache__/config.cpython-39.pyc,,
+flask_jwt_extended/__pycache__/default_callbacks.cpython-39.pyc,,
+flask_jwt_extended/__pycache__/exceptions.cpython-39.pyc,,
+flask_jwt_extended/__pycache__/internal_utils.cpython-39.pyc,,
+flask_jwt_extended/__pycache__/jwt_manager.cpython-39.pyc,,
+flask_jwt_extended/__pycache__/tokens.cpython-39.pyc,,
+flask_jwt_extended/__pycache__/typing.cpython-39.pyc,,
+flask_jwt_extended/__pycache__/utils.cpython-39.pyc,,
+flask_jwt_extended/__pycache__/view_decorators.cpython-39.pyc,,
+flask_jwt_extended/config.py,sha256=Ccg49Fl3xbNH7vz_gxc99--RSmJJKtpwQVn9-gtpYMQ,9937
+flask_jwt_extended/default_callbacks.py,sha256=e-DIYysG8bCCHeyDj5DGw1unMD1gBE8Wk7TCD01gs60,5273
+flask_jwt_extended/exceptions.py,sha256=kleki6CySt5u-BiAwJ-uskWb8rySnXHLC-w-YvMqyk4,2400
+flask_jwt_extended/internal_utils.py,sha256=Ux-hKTiaoOpaig_rwhh1_wck3X89Jba95na7o3bm1xg,1793
+flask_jwt_extended/jwt_manager.py,sha256=uVc_WJc3OKKS9F-gvmiBEE0Dlv5PoefQ5c6HJTdufHI,23134
+flask_jwt_extended/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+flask_jwt_extended/tokens.py,sha256=RMKdutK2mRGFyeKYfdD162mPBrQILogaCPwmt69R9gw,3044
+flask_jwt_extended/typing.py,sha256=pnC2fIzZE4nfTb1T32z-WvxYPoaxCWcqUIIcO07ZFGg,253
+flask_jwt_extended/utils.py,sha256=RoI87ZzSoJSbFQu-Fh4yUGIm43Ji7qtP0iM5Aj4aNPw,15845
+flask_jwt_extended/view_decorators.py,sha256=bm7jQiP0U0uor7LLj5q9s0ohvK5piW2wVJ5WNkANXG0,12960

+ 0 - 0
rest_venv/Lib/site-packages/PyJWT-2.4.0.dist-info/REQUESTED → rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/REQUESTED


+ 6 - 0
rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/WHEEL

@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+

+ 1 - 0
rest_venv/Lib/site-packages/Flask_JWT_Extended-4.4.1.dist-info/top_level.txt

@@ -0,0 +1 @@
+flask_jwt_extended

+ 2634 - 0
rest_venv/Lib/site-packages/OpenSSL/SSL.py

@@ -0,0 +1,2634 @@
+import os
+import socket
+from sys import platform
+from functools import wraps, partial
+from itertools import count, chain
+from weakref import WeakValueDictionary
+from errno import errorcode
+
+from OpenSSL._util import (
+    UNSPECIFIED as _UNSPECIFIED,
+    exception_from_error_queue as _exception_from_error_queue,
+    ffi as _ffi,
+    lib as _lib,
+    make_assert as _make_assert,
+    path_bytes as _path_bytes,
+    text_to_bytes_and_warn as _text_to_bytes_and_warn,
+    no_zero_allocator as _no_zero_allocator,
+)
+
+from OpenSSL.crypto import (
+    FILETYPE_PEM,
+    _PassphraseHelper,
+    PKey,
+    X509Name,
+    X509,
+    X509Store,
+)
+
+__all__ = [
+    "OPENSSL_VERSION_NUMBER",
+    "SSLEAY_VERSION",
+    "SSLEAY_CFLAGS",
+    "SSLEAY_PLATFORM",
+    "SSLEAY_DIR",
+    "SSLEAY_BUILT_ON",
+    "SENT_SHUTDOWN",
+    "RECEIVED_SHUTDOWN",
+    "SSLv2_METHOD",
+    "SSLv3_METHOD",
+    "SSLv23_METHOD",
+    "TLSv1_METHOD",
+    "TLSv1_1_METHOD",
+    "TLSv1_2_METHOD",
+    "TLS_METHOD",
+    "TLS_SERVER_METHOD",
+    "TLS_CLIENT_METHOD",
+    "DTLS_METHOD",
+    "DTLS_SERVER_METHOD",
+    "DTLS_CLIENT_METHOD",
+    "SSL3_VERSION",
+    "TLS1_VERSION",
+    "TLS1_1_VERSION",
+    "TLS1_2_VERSION",
+    "TLS1_3_VERSION",
+    "OP_NO_SSLv2",
+    "OP_NO_SSLv3",
+    "OP_NO_TLSv1",
+    "OP_NO_TLSv1_1",
+    "OP_NO_TLSv1_2",
+    "OP_NO_TLSv1_3",
+    "MODE_RELEASE_BUFFERS",
+    "OP_SINGLE_DH_USE",
+    "OP_SINGLE_ECDH_USE",
+    "OP_EPHEMERAL_RSA",
+    "OP_MICROSOFT_SESS_ID_BUG",
+    "OP_NETSCAPE_CHALLENGE_BUG",
+    "OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG",
+    "OP_SSLREF2_REUSE_CERT_TYPE_BUG",
+    "OP_MICROSOFT_BIG_SSLV3_BUFFER",
+    "OP_MSIE_SSLV2_RSA_PADDING",
+    "OP_SSLEAY_080_CLIENT_DH_BUG",
+    "OP_TLS_D5_BUG",
+    "OP_TLS_BLOCK_PADDING_BUG",
+    "OP_DONT_INSERT_EMPTY_FRAGMENTS",
+    "OP_CIPHER_SERVER_PREFERENCE",
+    "OP_TLS_ROLLBACK_BUG",
+    "OP_PKCS1_CHECK_1",
+    "OP_PKCS1_CHECK_2",
+    "OP_NETSCAPE_CA_DN_BUG",
+    "OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG",
+    "OP_NO_COMPRESSION",
+    "OP_NO_QUERY_MTU",
+    "OP_COOKIE_EXCHANGE",
+    "OP_NO_TICKET",
+    "OP_NO_RENEGOTIATION",
+    "OP_ALL",
+    "VERIFY_PEER",
+    "VERIFY_FAIL_IF_NO_PEER_CERT",
+    "VERIFY_CLIENT_ONCE",
+    "VERIFY_NONE",
+    "SESS_CACHE_OFF",
+    "SESS_CACHE_CLIENT",
+    "SESS_CACHE_SERVER",
+    "SESS_CACHE_BOTH",
+    "SESS_CACHE_NO_AUTO_CLEAR",
+    "SESS_CACHE_NO_INTERNAL_LOOKUP",
+    "SESS_CACHE_NO_INTERNAL_STORE",
+    "SESS_CACHE_NO_INTERNAL",
+    "SSL_ST_CONNECT",
+    "SSL_ST_ACCEPT",
+    "SSL_ST_MASK",
+    "SSL_CB_LOOP",
+    "SSL_CB_EXIT",
+    "SSL_CB_READ",
+    "SSL_CB_WRITE",
+    "SSL_CB_ALERT",
+    "SSL_CB_READ_ALERT",
+    "SSL_CB_WRITE_ALERT",
+    "SSL_CB_ACCEPT_LOOP",
+    "SSL_CB_ACCEPT_EXIT",
+    "SSL_CB_CONNECT_LOOP",
+    "SSL_CB_CONNECT_EXIT",
+    "SSL_CB_HANDSHAKE_START",
+    "SSL_CB_HANDSHAKE_DONE",
+    "Error",
+    "WantReadError",
+    "WantWriteError",
+    "WantX509LookupError",
+    "ZeroReturnError",
+    "SysCallError",
+    "NO_OVERLAPPING_PROTOCOLS",
+    "SSLeay_version",
+    "Session",
+    "Context",
+    "Connection",
+]
+
+
+OPENSSL_VERSION_NUMBER = _lib.OPENSSL_VERSION_NUMBER
+SSLEAY_VERSION = _lib.SSLEAY_VERSION
+SSLEAY_CFLAGS = _lib.SSLEAY_CFLAGS
+SSLEAY_PLATFORM = _lib.SSLEAY_PLATFORM
+SSLEAY_DIR = _lib.SSLEAY_DIR
+SSLEAY_BUILT_ON = _lib.SSLEAY_BUILT_ON
+
+SENT_SHUTDOWN = _lib.SSL_SENT_SHUTDOWN
+RECEIVED_SHUTDOWN = _lib.SSL_RECEIVED_SHUTDOWN
+
+SSLv2_METHOD = 1
+SSLv3_METHOD = 2
+SSLv23_METHOD = 3
+TLSv1_METHOD = 4
+TLSv1_1_METHOD = 5
+TLSv1_2_METHOD = 6
+TLS_METHOD = 7
+TLS_SERVER_METHOD = 8
+TLS_CLIENT_METHOD = 9
+DTLS_METHOD = 10
+DTLS_SERVER_METHOD = 11
+DTLS_CLIENT_METHOD = 12
+
+try:
+    SSL3_VERSION = _lib.SSL3_VERSION
+    TLS1_VERSION = _lib.TLS1_VERSION
+    TLS1_1_VERSION = _lib.TLS1_1_VERSION
+    TLS1_2_VERSION = _lib.TLS1_2_VERSION
+    TLS1_3_VERSION = _lib.TLS1_3_VERSION
+except AttributeError:
+    # Hardcode constants for cryptography < 3.4, see
+    # https://github.com/pyca/pyopenssl/pull/985#issuecomment-775186682
+    SSL3_VERSION = 768
+    TLS1_VERSION = 769
+    TLS1_1_VERSION = 770
+    TLS1_2_VERSION = 771
+    TLS1_3_VERSION = 772
+
+OP_NO_SSLv2 = _lib.SSL_OP_NO_SSLv2
+OP_NO_SSLv3 = _lib.SSL_OP_NO_SSLv3
+OP_NO_TLSv1 = _lib.SSL_OP_NO_TLSv1
+OP_NO_TLSv1_1 = _lib.SSL_OP_NO_TLSv1_1
+OP_NO_TLSv1_2 = _lib.SSL_OP_NO_TLSv1_2
+try:
+    OP_NO_TLSv1_3 = _lib.SSL_OP_NO_TLSv1_3
+except AttributeError:
+    pass
+
+MODE_RELEASE_BUFFERS = _lib.SSL_MODE_RELEASE_BUFFERS
+
+OP_SINGLE_DH_USE = _lib.SSL_OP_SINGLE_DH_USE
+OP_SINGLE_ECDH_USE = _lib.SSL_OP_SINGLE_ECDH_USE
+OP_EPHEMERAL_RSA = _lib.SSL_OP_EPHEMERAL_RSA
+OP_MICROSOFT_SESS_ID_BUG = _lib.SSL_OP_MICROSOFT_SESS_ID_BUG
+OP_NETSCAPE_CHALLENGE_BUG = _lib.SSL_OP_NETSCAPE_CHALLENGE_BUG
+OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = (
+    _lib.SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+)
+OP_SSLREF2_REUSE_CERT_TYPE_BUG = _lib.SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
+OP_MICROSOFT_BIG_SSLV3_BUFFER = _lib.SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
+OP_MSIE_SSLV2_RSA_PADDING = _lib.SSL_OP_MSIE_SSLV2_RSA_PADDING
+OP_SSLEAY_080_CLIENT_DH_BUG = _lib.SSL_OP_SSLEAY_080_CLIENT_DH_BUG
+OP_TLS_D5_BUG = _lib.SSL_OP_TLS_D5_BUG
+OP_TLS_BLOCK_PADDING_BUG = _lib.SSL_OP_TLS_BLOCK_PADDING_BUG
+OP_DONT_INSERT_EMPTY_FRAGMENTS = _lib.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+OP_CIPHER_SERVER_PREFERENCE = _lib.SSL_OP_CIPHER_SERVER_PREFERENCE
+OP_TLS_ROLLBACK_BUG = _lib.SSL_OP_TLS_ROLLBACK_BUG
+OP_PKCS1_CHECK_1 = _lib.SSL_OP_PKCS1_CHECK_1
+OP_PKCS1_CHECK_2 = _lib.SSL_OP_PKCS1_CHECK_2
+OP_NETSCAPE_CA_DN_BUG = _lib.SSL_OP_NETSCAPE_CA_DN_BUG
+OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = (
+    _lib.SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
+)
+OP_NO_COMPRESSION = _lib.SSL_OP_NO_COMPRESSION
+
+OP_NO_QUERY_MTU = _lib.SSL_OP_NO_QUERY_MTU
+OP_COOKIE_EXCHANGE = _lib.SSL_OP_COOKIE_EXCHANGE
+OP_NO_TICKET = _lib.SSL_OP_NO_TICKET
+
+try:
+    OP_NO_RENEGOTIATION = _lib.SSL_OP_NO_RENEGOTIATION
+except AttributeError:
+    pass
+
+OP_ALL = _lib.SSL_OP_ALL
+
+VERIFY_PEER = _lib.SSL_VERIFY_PEER
+VERIFY_FAIL_IF_NO_PEER_CERT = _lib.SSL_VERIFY_FAIL_IF_NO_PEER_CERT
+VERIFY_CLIENT_ONCE = _lib.SSL_VERIFY_CLIENT_ONCE
+VERIFY_NONE = _lib.SSL_VERIFY_NONE
+
+SESS_CACHE_OFF = _lib.SSL_SESS_CACHE_OFF
+SESS_CACHE_CLIENT = _lib.SSL_SESS_CACHE_CLIENT
+SESS_CACHE_SERVER = _lib.SSL_SESS_CACHE_SERVER
+SESS_CACHE_BOTH = _lib.SSL_SESS_CACHE_BOTH
+SESS_CACHE_NO_AUTO_CLEAR = _lib.SSL_SESS_CACHE_NO_AUTO_CLEAR
+SESS_CACHE_NO_INTERNAL_LOOKUP = _lib.SSL_SESS_CACHE_NO_INTERNAL_LOOKUP
+SESS_CACHE_NO_INTERNAL_STORE = _lib.SSL_SESS_CACHE_NO_INTERNAL_STORE
+SESS_CACHE_NO_INTERNAL = _lib.SSL_SESS_CACHE_NO_INTERNAL
+
+SSL_ST_CONNECT = _lib.SSL_ST_CONNECT
+SSL_ST_ACCEPT = _lib.SSL_ST_ACCEPT
+SSL_ST_MASK = _lib.SSL_ST_MASK
+
+SSL_CB_LOOP = _lib.SSL_CB_LOOP
+SSL_CB_EXIT = _lib.SSL_CB_EXIT
+SSL_CB_READ = _lib.SSL_CB_READ
+SSL_CB_WRITE = _lib.SSL_CB_WRITE
+SSL_CB_ALERT = _lib.SSL_CB_ALERT
+SSL_CB_READ_ALERT = _lib.SSL_CB_READ_ALERT
+SSL_CB_WRITE_ALERT = _lib.SSL_CB_WRITE_ALERT
+SSL_CB_ACCEPT_LOOP = _lib.SSL_CB_ACCEPT_LOOP
+SSL_CB_ACCEPT_EXIT = _lib.SSL_CB_ACCEPT_EXIT
+SSL_CB_CONNECT_LOOP = _lib.SSL_CB_CONNECT_LOOP
+SSL_CB_CONNECT_EXIT = _lib.SSL_CB_CONNECT_EXIT
+SSL_CB_HANDSHAKE_START = _lib.SSL_CB_HANDSHAKE_START
+SSL_CB_HANDSHAKE_DONE = _lib.SSL_CB_HANDSHAKE_DONE
+
+# Taken from https://golang.org/src/crypto/x509/root_linux.go
+_CERTIFICATE_FILE_LOCATIONS = [
+    "/etc/ssl/certs/ca-certificates.crt",  # Debian/Ubuntu/Gentoo etc.
+    "/etc/pki/tls/certs/ca-bundle.crt",  # Fedora/RHEL 6
+    "/etc/ssl/ca-bundle.pem",  # OpenSUSE
+    "/etc/pki/tls/cacert.pem",  # OpenELEC
+    "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",  # CentOS/RHEL 7
+]
+
+_CERTIFICATE_PATH_LOCATIONS = [
+    "/etc/ssl/certs",  # SLES10/SLES11
+]
+
+# These values are compared to output from cffi's ffi.string so they must be
+# byte strings.
+_CRYPTOGRAPHY_MANYLINUX_CA_DIR = b"/opt/pyca/cryptography/openssl/certs"
+_CRYPTOGRAPHY_MANYLINUX_CA_FILE = b"/opt/pyca/cryptography/openssl/cert.pem"
+
+
+class Error(Exception):
+    """
+    An error occurred in an `OpenSSL.SSL` API.
+    """
+
+
+_raise_current_error = partial(_exception_from_error_queue, Error)
+_openssl_assert = _make_assert(Error)
+
+
+class WantReadError(Error):
+    pass
+
+
+class WantWriteError(Error):
+    pass
+
+
+class WantX509LookupError(Error):
+    pass
+
+
+class ZeroReturnError(Error):
+    pass
+
+
+class SysCallError(Error):
+    pass
+
+
+class _CallbackExceptionHelper(object):
+    """
+    A base class for wrapper classes that allow for intelligent exception
+    handling in OpenSSL callbacks.
+
+    :ivar list _problems: Any exceptions that occurred while executing in a
+        context where they could not be raised in the normal way.  Typically
+        this is because OpenSSL has called into some Python code and requires a
+        return value.  The exceptions are saved to be raised later when it is
+        possible to do so.
+    """
+
+    def __init__(self):
+        self._problems = []
+
+    def raise_if_problem(self):
+        """
+        Raise an exception from the OpenSSL error queue or that was previously
+        captured whe running a callback.
+        """
+        if self._problems:
+            try:
+                _raise_current_error()
+            except Error:
+                pass
+            raise self._problems.pop(0)
+
+
+class _VerifyHelper(_CallbackExceptionHelper):
+    """
+    Wrap a callback such that it can be used as a certificate verification
+    callback.
+    """
+
+    def __init__(self, callback):
+        _CallbackExceptionHelper.__init__(self)
+
+        @wraps(callback)
+        def wrapper(ok, store_ctx):
+            x509 = _lib.X509_STORE_CTX_get_current_cert(store_ctx)
+            _lib.X509_up_ref(x509)
+            cert = X509._from_raw_x509_ptr(x509)
+            error_number = _lib.X509_STORE_CTX_get_error(store_ctx)
+            error_depth = _lib.X509_STORE_CTX_get_error_depth(store_ctx)
+
+            index = _lib.SSL_get_ex_data_X509_STORE_CTX_idx()
+            ssl = _lib.X509_STORE_CTX_get_ex_data(store_ctx, index)
+            connection = Connection._reverse_mapping[ssl]
+
+            try:
+                result = callback(
+                    connection, cert, error_number, error_depth, ok
+                )
+            except Exception as e:
+                self._problems.append(e)
+                return 0
+            else:
+                if result:
+                    _lib.X509_STORE_CTX_set_error(store_ctx, _lib.X509_V_OK)
+                    return 1
+                else:
+                    return 0
+
+        self.callback = _ffi.callback(
+            "int (*)(int, X509_STORE_CTX *)", wrapper
+        )
+
+
+NO_OVERLAPPING_PROTOCOLS = object()
+
+
+class _ALPNSelectHelper(_CallbackExceptionHelper):
+    """
+    Wrap a callback such that it can be used as an ALPN selection callback.
+    """
+
+    def __init__(self, callback):
+        _CallbackExceptionHelper.__init__(self)
+
+        @wraps(callback)
+        def wrapper(ssl, out, outlen, in_, inlen, arg):
+            try:
+                conn = Connection._reverse_mapping[ssl]
+
+                # The string passed to us is made up of multiple
+                # length-prefixed bytestrings. We need to split that into a
+                # list.
+                instr = _ffi.buffer(in_, inlen)[:]
+                protolist = []
+                while instr:
+                    encoded_len = instr[0]
+                    proto = instr[1 : encoded_len + 1]
+                    protolist.append(proto)
+                    instr = instr[encoded_len + 1 :]
+
+                # Call the callback
+                outbytes = callback(conn, protolist)
+                any_accepted = True
+                if outbytes is NO_OVERLAPPING_PROTOCOLS:
+                    outbytes = b""
+                    any_accepted = False
+                elif not isinstance(outbytes, bytes):
+                    raise TypeError(
+                        "ALPN callback must return a bytestring or the "
+                        "special NO_OVERLAPPING_PROTOCOLS sentinel value."
+                    )
+
+                # Save our callback arguments on the connection object to make
+                # sure that they don't get freed before OpenSSL can use them.
+                # Then, return them in the appropriate output parameters.
+                conn._alpn_select_callback_args = [
+                    _ffi.new("unsigned char *", len(outbytes)),
+                    _ffi.new("unsigned char[]", outbytes),
+                ]
+                outlen[0] = conn._alpn_select_callback_args[0][0]
+                out[0] = conn._alpn_select_callback_args[1]
+                if not any_accepted:
+                    return _lib.SSL_TLSEXT_ERR_NOACK
+                return _lib.SSL_TLSEXT_ERR_OK
+            except Exception as e:
+                self._problems.append(e)
+                return _lib.SSL_TLSEXT_ERR_ALERT_FATAL
+
+        self.callback = _ffi.callback(
+            (
+                "int (*)(SSL *, unsigned char **, unsigned char *, "
+                "const unsigned char *, unsigned int, void *)"
+            ),
+            wrapper,
+        )
+
+
+class _OCSPServerCallbackHelper(_CallbackExceptionHelper):
+    """
+    Wrap a callback such that it can be used as an OCSP callback for the server
+    side.
+
+    Annoyingly, OpenSSL defines one OCSP callback but uses it in two different
+    ways. For servers, that callback is expected to retrieve some OCSP data and
+    hand it to OpenSSL, and may return only SSL_TLSEXT_ERR_OK,
+    SSL_TLSEXT_ERR_FATAL, and SSL_TLSEXT_ERR_NOACK. For clients, that callback
+    is expected to check the OCSP data, and returns a negative value on error,
+    0 if the response is not acceptable, or positive if it is. These are
+    mutually exclusive return code behaviours, and they mean that we need two
+    helpers so that we always return an appropriate error code if the user's
+    code throws an exception.
+
+    Given that we have to have two helpers anyway, these helpers are a bit more
+    helpery than most: specifically, they hide a few more of the OpenSSL
+    functions so that the user has an easier time writing these callbacks.
+
+    This helper implements the server side.
+    """
+
+    def __init__(self, callback):
+        _CallbackExceptionHelper.__init__(self)
+
+        @wraps(callback)
+        def wrapper(ssl, cdata):
+            try:
+                conn = Connection._reverse_mapping[ssl]
+
+                # Extract the data if any was provided.
+                if cdata != _ffi.NULL:
+                    data = _ffi.from_handle(cdata)
+                else:
+                    data = None
+
+                # Call the callback.
+                ocsp_data = callback(conn, data)
+
+                if not isinstance(ocsp_data, bytes):
+                    raise TypeError("OCSP callback must return a bytestring.")
+
+                # If the OCSP data was provided, we will pass it to OpenSSL.
+                # However, we have an early exit here: if no OCSP data was
+                # provided we will just exit out and tell OpenSSL that there
+                # is nothing to do.
+                if not ocsp_data:
+                    return 3  # SSL_TLSEXT_ERR_NOACK
+
+                # OpenSSL takes ownership of this data and expects it to have
+                # been allocated by OPENSSL_malloc.
+                ocsp_data_length = len(ocsp_data)
+                data_ptr = _lib.OPENSSL_malloc(ocsp_data_length)
+                _ffi.buffer(data_ptr, ocsp_data_length)[:] = ocsp_data
+
+                _lib.SSL_set_tlsext_status_ocsp_resp(
+                    ssl, data_ptr, ocsp_data_length
+                )
+
+                return 0
+            except Exception as e:
+                self._problems.append(e)
+                return 2  # SSL_TLSEXT_ERR_ALERT_FATAL
+
+        self.callback = _ffi.callback("int (*)(SSL *, void *)", wrapper)
+
+
+class _OCSPClientCallbackHelper(_CallbackExceptionHelper):
+    """
+    Wrap a callback such that it can be used as an OCSP callback for the client
+    side.
+
+    Annoyingly, OpenSSL defines one OCSP callback but uses it in two different
+    ways. For servers, that callback is expected to retrieve some OCSP data and
+    hand it to OpenSSL, and may return only SSL_TLSEXT_ERR_OK,
+    SSL_TLSEXT_ERR_FATAL, and SSL_TLSEXT_ERR_NOACK. For clients, that callback
+    is expected to check the OCSP data, and returns a negative value on error,
+    0 if the response is not acceptable, or positive if it is. These are
+    mutually exclusive return code behaviours, and they mean that we need two
+    helpers so that we always return an appropriate error code if the user's
+    code throws an exception.
+
+    Given that we have to have two helpers anyway, these helpers are a bit more
+    helpery than most: specifically, they hide a few more of the OpenSSL
+    functions so that the user has an easier time writing these callbacks.
+
+    This helper implements the client side.
+    """
+
+    def __init__(self, callback):
+        _CallbackExceptionHelper.__init__(self)
+
+        @wraps(callback)
+        def wrapper(ssl, cdata):
+            try:
+                conn = Connection._reverse_mapping[ssl]
+
+                # Extract the data if any was provided.
+                if cdata != _ffi.NULL:
+                    data = _ffi.from_handle(cdata)
+                else:
+                    data = None
+
+                # Get the OCSP data.
+                ocsp_ptr = _ffi.new("unsigned char **")
+                ocsp_len = _lib.SSL_get_tlsext_status_ocsp_resp(ssl, ocsp_ptr)
+                if ocsp_len < 0:
+                    # No OCSP data.
+                    ocsp_data = b""
+                else:
+                    # Copy the OCSP data, then pass it to the callback.
+                    ocsp_data = _ffi.buffer(ocsp_ptr[0], ocsp_len)[:]
+
+                valid = callback(conn, ocsp_data, data)
+
+                # Return 1 on success or 0 on error.
+                return int(bool(valid))
+
+            except Exception as e:
+                self._problems.append(e)
+                # Return negative value if an exception is hit.
+                return -1
+
+        self.callback = _ffi.callback("int (*)(SSL *, void *)", wrapper)
+
+
+class _CookieGenerateCallbackHelper(_CallbackExceptionHelper):
+    def __init__(self, callback):
+        _CallbackExceptionHelper.__init__(self)
+
+        @wraps(callback)
+        def wrapper(ssl, out, outlen):
+            try:
+                conn = Connection._reverse_mapping[ssl]
+                cookie = callback(conn)
+                out[0 : len(cookie)] = cookie
+                outlen[0] = len(cookie)
+                return 1
+            except Exception as e:
+                self._problems.append(e)
+                # "a zero return value can be used to abort the handshake"
+                return 0
+
+        self.callback = _ffi.callback(
+            "int (*)(SSL *, unsigned char *, unsigned int *)",
+            wrapper,
+        )
+
+
+class _CookieVerifyCallbackHelper(_CallbackExceptionHelper):
+    def __init__(self, callback):
+        _CallbackExceptionHelper.__init__(self)
+
+        @wraps(callback)
+        def wrapper(ssl, c_cookie, cookie_len):
+            try:
+                conn = Connection._reverse_mapping[ssl]
+                return callback(conn, bytes(c_cookie[0:cookie_len]))
+            except Exception as e:
+                self._problems.append(e)
+                return 0
+
+        self.callback = _ffi.callback(
+            "int (*)(SSL *, unsigned char *, unsigned int)",
+            wrapper,
+        )
+
+
+def _asFileDescriptor(obj):
+    fd = None
+    if not isinstance(obj, int):
+        meth = getattr(obj, "fileno", None)
+        if meth is not None:
+            obj = meth()
+
+    if isinstance(obj, int):
+        fd = obj
+
+    if not isinstance(fd, int):
+        raise TypeError("argument must be an int, or have a fileno() method.")
+    elif fd < 0:
+        raise ValueError(
+            "file descriptor cannot be a negative integer (%i)" % (fd,)
+        )
+
+    return fd
+
+
+def SSLeay_version(type):
+    """
+    Return a string describing the version of OpenSSL in use.
+
+    :param type: One of the :const:`SSLEAY_` constants defined in this module.
+    """
+    return _ffi.string(_lib.SSLeay_version(type))
+
+
+def _make_requires(flag, error):
+    """
+    Builds a decorator that ensures that functions that rely on OpenSSL
+    functions that are not present in this build raise NotImplementedError,
+    rather than AttributeError coming out of cryptography.
+
+    :param flag: A cryptography flag that guards the functions, e.g.
+        ``Cryptography_HAS_NEXTPROTONEG``.
+    :param error: The string to be used in the exception if the flag is false.
+    """
+
+    def _requires_decorator(func):
+        if not flag:
+
+            @wraps(func)
+            def explode(*args, **kwargs):
+                raise NotImplementedError(error)
+
+            return explode
+        else:
+            return func
+
+    return _requires_decorator
+
+
+_requires_alpn = _make_requires(
+    _lib.Cryptography_HAS_ALPN, "ALPN not available"
+)
+
+
+_requires_keylog = _make_requires(
+    getattr(_lib, "Cryptography_HAS_KEYLOG", None), "Key logging not available"
+)
+
+
+class Session(object):
+    """
+    A class representing an SSL session.  A session defines certain connection
+    parameters which may be re-used to speed up the setup of subsequent
+    connections.
+
+    .. versionadded:: 0.14
+    """
+
+    pass
+
+
+class Context(object):
+    """
+    :class:`OpenSSL.SSL.Context` instances define the parameters for setting
+    up new SSL connections.
+
+    :param method: One of TLS_METHOD, TLS_CLIENT_METHOD, TLS_SERVER_METHOD,
+                   DTLS_METHOD, DTLS_CLIENT_METHOD, or DTLS_SERVER_METHOD.
+                   SSLv23_METHOD, TLSv1_METHOD, etc. are deprecated and should
+                   not be used.
+    """
+
+    _methods = {
+        SSLv2_METHOD: "SSLv2_method",
+        SSLv3_METHOD: "SSLv3_method",
+        SSLv23_METHOD: "SSLv23_method",
+        TLSv1_METHOD: "TLSv1_method",
+        TLSv1_1_METHOD: "TLSv1_1_method",
+        TLSv1_2_METHOD: "TLSv1_2_method",
+        TLS_METHOD: "TLS_method",
+        TLS_SERVER_METHOD: "TLS_server_method",
+        TLS_CLIENT_METHOD: "TLS_client_method",
+        DTLS_METHOD: "DTLS_method",
+        DTLS_SERVER_METHOD: "DTLS_server_method",
+        DTLS_CLIENT_METHOD: "DTLS_client_method",
+    }
+    _methods = dict(
+        (identifier, getattr(_lib, name))
+        for (identifier, name) in _methods.items()
+        if getattr(_lib, name, None) is not None
+    )
+
+    def __init__(self, method):
+        if not isinstance(method, int):
+            raise TypeError("method must be an integer")
+
+        try:
+            method_func = self._methods[method]
+        except KeyError:
+            raise ValueError("No such protocol")
+
+        method_obj = method_func()
+        _openssl_assert(method_obj != _ffi.NULL)
+
+        context = _lib.SSL_CTX_new(method_obj)
+        _openssl_assert(context != _ffi.NULL)
+        context = _ffi.gc(context, _lib.SSL_CTX_free)
+
+        self._context = context
+        self._passphrase_helper = None
+        self._passphrase_callback = None
+        self._passphrase_userdata = None
+        self._verify_helper = None
+        self._verify_callback = None
+        self._info_callback = None
+        self._keylog_callback = None
+        self._tlsext_servername_callback = None
+        self._app_data = None
+        self._alpn_select_helper = None
+        self._alpn_select_callback = None
+        self._ocsp_helper = None
+        self._ocsp_callback = None
+        self._ocsp_data = None
+        self._cookie_generate_helper = None
+        self._cookie_verify_helper = None
+
+        self.set_mode(_lib.SSL_MODE_ENABLE_PARTIAL_WRITE)
+
+    def set_min_proto_version(self, version):
+        """
+        Set the minimum supported protocol version. Setting the minimum
+        version to 0 will enable protocol versions down to the lowest version
+        supported by the library.
+
+        If the underlying OpenSSL build is missing support for the selected
+        version, this method will raise an exception.
+        """
+        _openssl_assert(
+            _lib.SSL_CTX_set_min_proto_version(self._context, version) == 1
+        )
+
+    def set_max_proto_version(self, version):
+        """
+        Set the maximum supported protocol version. Setting the maximum
+        version to 0 will enable protocol versions up to the highest version
+        supported by the library.
+
+        If the underlying OpenSSL build is missing support for the selected
+        version, this method will raise an exception.
+        """
+        _openssl_assert(
+            _lib.SSL_CTX_set_max_proto_version(self._context, version) == 1
+        )
+
+    def load_verify_locations(self, cafile, capath=None):
+        """
+        Let SSL know where we can find trusted certificates for the certificate
+        chain.  Note that the certificates have to be in PEM format.
+
+        If capath is passed, it must be a directory prepared using the
+        ``c_rehash`` tool included with OpenSSL.  Either, but not both, of
+        *pemfile* or *capath* may be :data:`None`.
+
+        :param cafile: In which file we can find the certificates (``bytes`` or
+            ``unicode``).
+        :param capath: In which directory we can find the certificates
+            (``bytes`` or ``unicode``).
+
+        :return: None
+        """
+        if cafile is None:
+            cafile = _ffi.NULL
+        else:
+            cafile = _path_bytes(cafile)
+
+        if capath is None:
+            capath = _ffi.NULL
+        else:
+            capath = _path_bytes(capath)
+
+        load_result = _lib.SSL_CTX_load_verify_locations(
+            self._context, cafile, capath
+        )
+        if not load_result:
+            _raise_current_error()
+
+    def _wrap_callback(self, callback):
+        @wraps(callback)
+        def wrapper(size, verify, userdata):
+            return callback(size, verify, self._passphrase_userdata)
+
+        return _PassphraseHelper(
+            FILETYPE_PEM, wrapper, more_args=True, truncate=True
+        )
+
+    def set_passwd_cb(self, callback, userdata=None):
+        """
+        Set the passphrase callback.  This function will be called
+        when a private key with a passphrase is loaded.
+
+        :param callback: The Python callback to use.  This must accept three
+            positional arguments.  First, an integer giving the maximum length
+            of the passphrase it may return.  If the returned passphrase is
+            longer than this, it will be truncated.  Second, a boolean value
+            which will be true if the user should be prompted for the
+            passphrase twice and the callback should verify that the two values
+            supplied are equal. Third, the value given as the *userdata*
+            parameter to :meth:`set_passwd_cb`.  The *callback* must return
+            a byte string. If an error occurs, *callback* should return a false
+            value (e.g. an empty string).
+        :param userdata: (optional) A Python object which will be given as
+                         argument to the callback
+        :return: None
+        """
+        if not callable(callback):
+            raise TypeError("callback must be callable")
+
+        self._passphrase_helper = self._wrap_callback(callback)
+        self._passphrase_callback = self._passphrase_helper.callback
+        _lib.SSL_CTX_set_default_passwd_cb(
+            self._context, self._passphrase_callback
+        )
+        self._passphrase_userdata = userdata
+
+    def set_default_verify_paths(self):
+        """
+        Specify that the platform provided CA certificates are to be used for
+        verification purposes. This method has some caveats related to the
+        binary wheels that cryptography (pyOpenSSL's primary dependency) ships:
+
+        *   macOS will only load certificates using this method if the user has
+            the ``openssl@1.1`` `Homebrew <https://brew.sh>`_ formula installed
+            in the default location.
+        *   Windows will not work.
+        *   manylinux1 cryptography wheels will work on most common Linux
+            distributions in pyOpenSSL 17.1.0 and above.  pyOpenSSL detects the
+            manylinux1 wheel and attempts to load roots via a fallback path.
+
+        :return: None
+        """
+        # SSL_CTX_set_default_verify_paths will attempt to load certs from
+        # both a cafile and capath that are set at compile time. However,
+        # it will first check environment variables and, if present, load
+        # those paths instead
+        set_result = _lib.SSL_CTX_set_default_verify_paths(self._context)
+        _openssl_assert(set_result == 1)
+        # After attempting to set default_verify_paths we need to know whether
+        # to go down the fallback path.
+        # First we'll check to see if any env vars have been set. If so,
+        # we won't try to do anything else because the user has set the path
+        # themselves.
+        dir_env_var = _ffi.string(_lib.X509_get_default_cert_dir_env()).decode(
+            "ascii"
+        )
+        file_env_var = _ffi.string(
+            _lib.X509_get_default_cert_file_env()
+        ).decode("ascii")
+        if not self._check_env_vars_set(dir_env_var, file_env_var):
+            default_dir = _ffi.string(_lib.X509_get_default_cert_dir())
+            default_file = _ffi.string(_lib.X509_get_default_cert_file())
+            # Now we check to see if the default_dir and default_file are set
+            # to the exact values we use in our manylinux1 builds. If they are
+            # then we know to load the fallbacks
+            if (
+                default_dir == _CRYPTOGRAPHY_MANYLINUX_CA_DIR
+                and default_file == _CRYPTOGRAPHY_MANYLINUX_CA_FILE
+            ):
+                # This is manylinux1, let's load our fallback paths
+                self._fallback_default_verify_paths(
+                    _CERTIFICATE_FILE_LOCATIONS, _CERTIFICATE_PATH_LOCATIONS
+                )
+
+    def _check_env_vars_set(self, dir_env_var, file_env_var):
+        """
+        Check to see if the default cert dir/file environment vars are present.
+
+        :return: bool
+        """
+        return (
+            os.environ.get(file_env_var) is not None
+            or os.environ.get(dir_env_var) is not None
+        )
+
+    def _fallback_default_verify_paths(self, file_path, dir_path):
+        """
+        Default verify paths are based on the compiled version of OpenSSL.
+        However, when pyca/cryptography is compiled as a manylinux1 wheel
+        that compiled location can potentially be wrong. So, like Go, we
+        will try a predefined set of paths and attempt to load roots
+        from there.
+
+        :return: None
+        """
+        for cafile in file_path:
+            if os.path.isfile(cafile):
+                self.load_verify_locations(cafile)
+                break
+
+        for capath in dir_path:
+            if os.path.isdir(capath):
+                self.load_verify_locations(None, capath)
+                break
+
+    def use_certificate_chain_file(self, certfile):
+        """
+        Load a certificate chain from a file.
+
+        :param certfile: The name of the certificate chain file (``bytes`` or
+            ``unicode``).  Must be PEM encoded.
+
+        :return: None
+        """
+        certfile = _path_bytes(certfile)
+
+        result = _lib.SSL_CTX_use_certificate_chain_file(
+            self._context, certfile
+        )
+        if not result:
+            _raise_current_error()
+
+    def use_certificate_file(self, certfile, filetype=FILETYPE_PEM):
+        """
+        Load a certificate from a file
+
+        :param certfile: The name of the certificate file (``bytes`` or
+            ``unicode``).
+        :param filetype: (optional) The encoding of the file, which is either
+            :const:`FILETYPE_PEM` or :const:`FILETYPE_ASN1`.  The default is
+            :const:`FILETYPE_PEM`.
+
+        :return: None
+        """
+        certfile = _path_bytes(certfile)
+        if not isinstance(filetype, int):
+            raise TypeError("filetype must be an integer")
+
+        use_result = _lib.SSL_CTX_use_certificate_file(
+            self._context, certfile, filetype
+        )
+        if not use_result:
+            _raise_current_error()
+
+    def use_certificate(self, cert):
+        """
+        Load a certificate from a X509 object
+
+        :param cert: The X509 object
+        :return: None
+        """
+        if not isinstance(cert, X509):
+            raise TypeError("cert must be an X509 instance")
+
+        use_result = _lib.SSL_CTX_use_certificate(self._context, cert._x509)
+        if not use_result:
+            _raise_current_error()
+
+    def add_extra_chain_cert(self, certobj):
+        """
+        Add certificate to chain
+
+        :param certobj: The X509 certificate object to add to the chain
+        :return: None
+        """
+        if not isinstance(certobj, X509):
+            raise TypeError("certobj must be an X509 instance")
+
+        copy = _lib.X509_dup(certobj._x509)
+        add_result = _lib.SSL_CTX_add_extra_chain_cert(self._context, copy)
+        if not add_result:
+            # TODO: This is untested.
+            _lib.X509_free(copy)
+            _raise_current_error()
+
+    def _raise_passphrase_exception(self):
+        if self._passphrase_helper is not None:
+            self._passphrase_helper.raise_if_problem(Error)
+
+        _raise_current_error()
+
+    def use_privatekey_file(self, keyfile, filetype=_UNSPECIFIED):
+        """
+        Load a private key from a file
+
+        :param keyfile: The name of the key file (``bytes`` or ``unicode``)
+        :param filetype: (optional) The encoding of the file, which is either
+            :const:`FILETYPE_PEM` or :const:`FILETYPE_ASN1`.  The default is
+            :const:`FILETYPE_PEM`.
+
+        :return: None
+        """
+        keyfile = _path_bytes(keyfile)
+
+        if filetype is _UNSPECIFIED:
+            filetype = FILETYPE_PEM
+        elif not isinstance(filetype, int):
+            raise TypeError("filetype must be an integer")
+
+        use_result = _lib.SSL_CTX_use_PrivateKey_file(
+            self._context, keyfile, filetype
+        )
+        if not use_result:
+            self._raise_passphrase_exception()
+
+    def use_privatekey(self, pkey):
+        """
+        Load a private key from a PKey object
+
+        :param pkey: The PKey object
+        :return: None
+        """
+        if not isinstance(pkey, PKey):
+            raise TypeError("pkey must be a PKey instance")
+
+        use_result = _lib.SSL_CTX_use_PrivateKey(self._context, pkey._pkey)
+        if not use_result:
+            self._raise_passphrase_exception()
+
+    def check_privatekey(self):
+        """
+        Check if the private key (loaded with :meth:`use_privatekey`) matches
+        the certificate (loaded with :meth:`use_certificate`)
+
+        :return: :data:`None` (raises :exc:`Error` if something's wrong)
+        """
+        if not _lib.SSL_CTX_check_private_key(self._context):
+            _raise_current_error()
+
+    def load_client_ca(self, cafile):
+        """
+        Load the trusted certificates that will be sent to the client.  Does
+        not actually imply any of the certificates are trusted; that must be
+        configured separately.
+
+        :param bytes cafile: The path to a certificates file in PEM format.
+        :return: None
+        """
+        ca_list = _lib.SSL_load_client_CA_file(
+            _text_to_bytes_and_warn("cafile", cafile)
+        )
+        _openssl_assert(ca_list != _ffi.NULL)
+        _lib.SSL_CTX_set_client_CA_list(self._context, ca_list)
+
+    def set_session_id(self, buf):
+        """
+        Set the session id to *buf* within which a session can be reused for
+        this Context object.  This is needed when doing session resumption,
+        because there is no way for a stored session to know which Context
+        object it is associated with.
+
+        :param bytes buf: The session id.
+
+        :returns: None
+        """
+        buf = _text_to_bytes_and_warn("buf", buf)
+        _openssl_assert(
+            _lib.SSL_CTX_set_session_id_context(self._context, buf, len(buf))
+            == 1
+        )
+
+    def set_session_cache_mode(self, mode):
+        """
+        Set the behavior of the session cache used by all connections using
+        this Context.  The previously set mode is returned.  See
+        :const:`SESS_CACHE_*` for details about particular modes.
+
+        :param mode: One or more of the SESS_CACHE_* flags (combine using
+            bitwise or)
+        :returns: The previously set caching mode.
+
+        .. versionadded:: 0.14
+        """
+        if not isinstance(mode, int):
+            raise TypeError("mode must be an integer")
+
+        return _lib.SSL_CTX_set_session_cache_mode(self._context, mode)
+
+    def get_session_cache_mode(self):
+        """
+        Get the current session cache mode.
+
+        :returns: The currently used cache mode.
+
+        .. versionadded:: 0.14
+        """
+        return _lib.SSL_CTX_get_session_cache_mode(self._context)
+
+    def set_verify(self, mode, callback=None):
+        """
+        Set the verification flags for this Context object to *mode* and
+        specify that *callback* should be used for verification callbacks.
+
+        :param mode: The verify mode, this should be one of
+            :const:`VERIFY_NONE` and :const:`VERIFY_PEER`. If
+            :const:`VERIFY_PEER` is used, *mode* can be OR:ed with
+            :const:`VERIFY_FAIL_IF_NO_PEER_CERT` and
+            :const:`VERIFY_CLIENT_ONCE` to further control the behaviour.
+        :param callback: The optional Python verification callback to use.
+            This should take five arguments: A Connection object, an X509
+            object, and three integer variables, which are in turn potential
+            error number, error depth and return code. *callback* should
+            return True if verification passes and False otherwise.
+            If omitted, OpenSSL's default verification is used.
+        :return: None
+
+        See SSL_CTX_set_verify(3SSL) for further details.
+        """
+        if not isinstance(mode, int):
+            raise TypeError("mode must be an integer")
+
+        if callback is None:
+            self._verify_helper = None
+            self._verify_callback = None
+            _lib.SSL_CTX_set_verify(self._context, mode, _ffi.NULL)
+        else:
+            if not callable(callback):
+                raise TypeError("callback must be callable")
+
+            self._verify_helper = _VerifyHelper(callback)
+            self._verify_callback = self._verify_helper.callback
+            _lib.SSL_CTX_set_verify(self._context, mode, self._verify_callback)
+
+    def set_verify_depth(self, depth):
+        """
+        Set the maximum depth for the certificate chain verification that shall
+        be allowed for this Context object.
+
+        :param depth: An integer specifying the verify depth
+        :return: None
+        """
+        if not isinstance(depth, int):
+            raise TypeError("depth must be an integer")
+
+        _lib.SSL_CTX_set_verify_depth(self._context, depth)
+
+    def get_verify_mode(self):
+        """
+        Retrieve the Context object's verify mode, as set by
+        :meth:`set_verify`.
+
+        :return: The verify mode
+        """
+        return _lib.SSL_CTX_get_verify_mode(self._context)
+
+    def get_verify_depth(self):
+        """
+        Retrieve the Context object's verify depth, as set by
+        :meth:`set_verify_depth`.
+
+        :return: The verify depth
+        """
+        return _lib.SSL_CTX_get_verify_depth(self._context)
+
+    def load_tmp_dh(self, dhfile):
+        """
+        Load parameters for Ephemeral Diffie-Hellman
+
+        :param dhfile: The file to load EDH parameters from (``bytes`` or
+            ``unicode``).
+
+        :return: None
+        """
+        dhfile = _path_bytes(dhfile)
+
+        bio = _lib.BIO_new_file(dhfile, b"r")
+        if bio == _ffi.NULL:
+            _raise_current_error()
+        bio = _ffi.gc(bio, _lib.BIO_free)
+
+        dh = _lib.PEM_read_bio_DHparams(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
+        dh = _ffi.gc(dh, _lib.DH_free)
+        res = _lib.SSL_CTX_set_tmp_dh(self._context, dh)
+        _openssl_assert(res == 1)
+
+    def set_tmp_ecdh(self, curve):
+        """
+        Select a curve to use for ECDHE key exchange.
+
+        :param curve: A curve object to use as returned by either
+            :meth:`OpenSSL.crypto.get_elliptic_curve` or
+            :meth:`OpenSSL.crypto.get_elliptic_curves`.
+
+        :return: None
+        """
+        _lib.SSL_CTX_set_tmp_ecdh(self._context, curve._to_EC_KEY())
+
+    def set_cipher_list(self, cipher_list):
+        """
+        Set the list of ciphers to be used in this context.
+
+        See the OpenSSL manual for more information (e.g.
+        :manpage:`ciphers(1)`).
+
+        :param bytes cipher_list: An OpenSSL cipher string.
+        :return: None
+        """
+        cipher_list = _text_to_bytes_and_warn("cipher_list", cipher_list)
+
+        if not isinstance(cipher_list, bytes):
+            raise TypeError("cipher_list must be a byte string.")
+
+        _openssl_assert(
+            _lib.SSL_CTX_set_cipher_list(self._context, cipher_list) == 1
+        )
+        # In OpenSSL 1.1.1 setting the cipher list will always return TLS 1.3
+        # ciphers even if you pass an invalid cipher. Applications (like
+        # Twisted) have tests that depend on an error being raised if an
+        # invalid cipher string is passed, but without the following check
+        # for the TLS 1.3 specific cipher suites it would never error.
+        tmpconn = Connection(self, None)
+        if tmpconn.get_cipher_list() == [
+            "TLS_AES_256_GCM_SHA384",
+            "TLS_CHACHA20_POLY1305_SHA256",
+            "TLS_AES_128_GCM_SHA256",
+        ]:
+            raise Error(
+                [
+                    (
+                        "SSL routines",
+                        "SSL_CTX_set_cipher_list",
+                        "no cipher match",
+                    ),
+                ],
+            )
+
+    def set_client_ca_list(self, certificate_authorities):
+        """
+        Set the list of preferred client certificate signers for this server
+        context.
+
+        This list of certificate authorities will be sent to the client when
+        the server requests a client certificate.
+
+        :param certificate_authorities: a sequence of X509Names.
+        :return: None
+
+        .. versionadded:: 0.10
+        """
+        name_stack = _lib.sk_X509_NAME_new_null()
+        _openssl_assert(name_stack != _ffi.NULL)
+
+        try:
+            for ca_name in certificate_authorities:
+                if not isinstance(ca_name, X509Name):
+                    raise TypeError(
+                        "client CAs must be X509Name objects, not %s "
+                        "objects" % (type(ca_name).__name__,)
+                    )
+                copy = _lib.X509_NAME_dup(ca_name._name)
+                _openssl_assert(copy != _ffi.NULL)
+                push_result = _lib.sk_X509_NAME_push(name_stack, copy)
+                if not push_result:
+                    _lib.X509_NAME_free(copy)
+                    _raise_current_error()
+        except Exception:
+            _lib.sk_X509_NAME_free(name_stack)
+            raise
+
+        _lib.SSL_CTX_set_client_CA_list(self._context, name_stack)
+
+    def add_client_ca(self, certificate_authority):
+        """
+        Add the CA certificate to the list of preferred signers for this
+        context.
+
+        The list of certificate authorities will be sent to the client when the
+        server requests a client certificate.
+
+        :param certificate_authority: certificate authority's X509 certificate.
+        :return: None
+
+        .. versionadded:: 0.10
+        """
+        if not isinstance(certificate_authority, X509):
+            raise TypeError("certificate_authority must be an X509 instance")
+
+        add_result = _lib.SSL_CTX_add_client_CA(
+            self._context, certificate_authority._x509
+        )
+        _openssl_assert(add_result == 1)
+
+    def set_timeout(self, timeout):
+        """
+        Set the timeout for newly created sessions for this Context object to
+        *timeout*.  The default value is 300 seconds. See the OpenSSL manual
+        for more information (e.g. :manpage:`SSL_CTX_set_timeout(3)`).
+
+        :param timeout: The timeout in (whole) seconds
+        :return: The previous session timeout
+        """
+        if not isinstance(timeout, int):
+            raise TypeError("timeout must be an integer")
+
+        return _lib.SSL_CTX_set_timeout(self._context, timeout)
+
+    def get_timeout(self):
+        """
+        Retrieve session timeout, as set by :meth:`set_timeout`. The default
+        is 300 seconds.
+
+        :return: The session timeout
+        """
+        return _lib.SSL_CTX_get_timeout(self._context)
+
+    def set_info_callback(self, callback):
+        """
+        Set the information callback to *callback*. This function will be
+        called from time to time during SSL handshakes.
+
+        :param callback: The Python callback to use.  This should take three
+            arguments: a Connection object and two integers.  The first integer
+            specifies where in the SSL handshake the function was called, and
+            the other the return code from a (possibly failed) internal
+            function call.
+        :return: None
+        """
+
+        @wraps(callback)
+        def wrapper(ssl, where, return_code):
+            callback(Connection._reverse_mapping[ssl], where, return_code)
+
+        self._info_callback = _ffi.callback(
+            "void (*)(const SSL *, int, int)", wrapper
+        )
+        _lib.SSL_CTX_set_info_callback(self._context, self._info_callback)
+
+    @_requires_keylog
+    def set_keylog_callback(self, callback):
+        """
+        Set the TLS key logging callback to *callback*. This function will be
+        called whenever TLS key material is generated or received, in order
+        to allow applications to store this keying material for debugging
+        purposes.
+
+        :param callback: The Python callback to use.  This should take two
+            arguments: a Connection object and a bytestring that contains
+            the key material in the format used by NSS for its SSLKEYLOGFILE
+            debugging output.
+        :return: None
+        """
+
+        @wraps(callback)
+        def wrapper(ssl, line):
+            line = _ffi.string(line)
+            callback(Connection._reverse_mapping[ssl], line)
+
+        self._keylog_callback = _ffi.callback(
+            "void (*)(const SSL *, const char *)", wrapper
+        )
+        _lib.SSL_CTX_set_keylog_callback(self._context, self._keylog_callback)
+
+    def get_app_data(self):
+        """
+        Get the application data (supplied via :meth:`set_app_data()`)
+
+        :return: The application data
+        """
+        return self._app_data
+
+    def set_app_data(self, data):
+        """
+        Set the application data (will be returned from get_app_data())
+
+        :param data: Any Python object
+        :return: None
+        """
+        self._app_data = data
+
+    def get_cert_store(self):
+        """
+        Get the certificate store for the context.  This can be used to add
+        "trusted" certificates without using the
+        :meth:`load_verify_locations` method.
+
+        :return: A X509Store object or None if it does not have one.
+        """
+        store = _lib.SSL_CTX_get_cert_store(self._context)
+        if store == _ffi.NULL:
+            # TODO: This is untested.
+            return None
+
+        pystore = X509Store.__new__(X509Store)
+        pystore._store = store
+        return pystore
+
+    def set_options(self, options):
+        """
+        Add options. Options set before are not cleared!
+        This method should be used with the :const:`OP_*` constants.
+
+        :param options: The options to add.
+        :return: The new option bitmask.
+        """
+        if not isinstance(options, int):
+            raise TypeError("options must be an integer")
+
+        return _lib.SSL_CTX_set_options(self._context, options)
+
+    def set_mode(self, mode):
+        """
+        Add modes via bitmask. Modes set before are not cleared!  This method
+        should be used with the :const:`MODE_*` constants.
+
+        :param mode: The mode to add.
+        :return: The new mode bitmask.
+        """
+        if not isinstance(mode, int):
+            raise TypeError("mode must be an integer")
+
+        return _lib.SSL_CTX_set_mode(self._context, mode)
+
+    def set_tlsext_servername_callback(self, callback):
+        """
+        Specify a callback function to be called when clients specify a server
+        name.
+
+        :param callback: The callback function.  It will be invoked with one
+            argument, the Connection instance.
+
+        .. versionadded:: 0.13
+        """
+
+        @wraps(callback)
+        def wrapper(ssl, alert, arg):
+            callback(Connection._reverse_mapping[ssl])
+            return 0
+
+        self._tlsext_servername_callback = _ffi.callback(
+            "int (*)(SSL *, int *, void *)", wrapper
+        )
+        _lib.SSL_CTX_set_tlsext_servername_callback(
+            self._context, self._tlsext_servername_callback
+        )
+
+    def set_tlsext_use_srtp(self, profiles):
+        """
+        Enable support for negotiating SRTP keying material.
+
+        :param bytes profiles: A colon delimited list of protection profile
+            names, like ``b'SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32'``.
+        :return: None
+        """
+        if not isinstance(profiles, bytes):
+            raise TypeError("profiles must be a byte string.")
+
+        _openssl_assert(
+            _lib.SSL_CTX_set_tlsext_use_srtp(self._context, profiles) == 0
+        )
+
+    @_requires_alpn
+    def set_alpn_protos(self, protos):
+        """
+        Specify the protocols that the client is prepared to speak after the
+        TLS connection has been negotiated using Application Layer Protocol
+        Negotiation.
+
+        :param protos: A list of the protocols to be offered to the server.
+            This list should be a Python list of bytestrings representing the
+            protocols to offer, e.g. ``[b'http/1.1', b'spdy/2']``.
+        """
+        # Different versions of OpenSSL are inconsistent about how they handle
+        # empty proto lists (see #1043), so we avoid the problem entirely by
+        # rejecting them ourselves.
+        if not protos:
+            raise ValueError("at least one protocol must be specified")
+
+        # Take the list of protocols and join them together, prefixing them
+        # with their lengths.
+        protostr = b"".join(
+            chain.from_iterable((bytes((len(p),)), p) for p in protos)
+        )
+
+        # Build a C string from the list. We don't need to save this off
+        # because OpenSSL immediately copies the data out.
+        input_str = _ffi.new("unsigned char[]", protostr)
+
+        # https://www.openssl.org/docs/man1.1.0/man3/SSL_CTX_set_alpn_protos.html:
+        # SSL_CTX_set_alpn_protos() and SSL_set_alpn_protos()
+        # return 0 on success, and non-0 on failure.
+        # WARNING: these functions reverse the return value convention.
+        _openssl_assert(
+            _lib.SSL_CTX_set_alpn_protos(
+                self._context, input_str, len(protostr)
+            )
+            == 0
+        )
+
+    @_requires_alpn
+    def set_alpn_select_callback(self, callback):
+        """
+        Specify a callback function that will be called on the server when a
+        client offers protocols using ALPN.
+
+        :param callback: The callback function.  It will be invoked with two
+            arguments: the Connection, and a list of offered protocols as
+            bytestrings, e.g ``[b'http/1.1', b'spdy/2']``.  It can return
+            one of those bytestrings to indicate the chosen protocol, the
+            empty bytestring to terminate the TLS connection, or the
+            :py:obj:`NO_OVERLAPPING_PROTOCOLS` to indicate that no offered
+            protocol was selected, but that the connection should not be
+            aborted.
+        """
+        self._alpn_select_helper = _ALPNSelectHelper(callback)
+        self._alpn_select_callback = self._alpn_select_helper.callback
+        _lib.SSL_CTX_set_alpn_select_cb(
+            self._context, self._alpn_select_callback, _ffi.NULL
+        )
+
+    def _set_ocsp_callback(self, helper, data):
+        """
+        This internal helper does the common work for
+        ``set_ocsp_server_callback`` and ``set_ocsp_client_callback``, which is
+        almost all of it.
+        """
+        self._ocsp_helper = helper
+        self._ocsp_callback = helper.callback
+        if data is None:
+            self._ocsp_data = _ffi.NULL
+        else:
+            self._ocsp_data = _ffi.new_handle(data)
+
+        rc = _lib.SSL_CTX_set_tlsext_status_cb(
+            self._context, self._ocsp_callback
+        )
+        _openssl_assert(rc == 1)
+        rc = _lib.SSL_CTX_set_tlsext_status_arg(self._context, self._ocsp_data)
+        _openssl_assert(rc == 1)
+
+    def set_ocsp_server_callback(self, callback, data=None):
+        """
+        Set a callback to provide OCSP data to be stapled to the TLS handshake
+        on the server side.
+
+        :param callback: The callback function. It will be invoked with two
+            arguments: the Connection, and the optional arbitrary data you have
+            provided. The callback must return a bytestring that contains the
+            OCSP data to staple to the handshake. If no OCSP data is available
+            for this connection, return the empty bytestring.
+        :param data: Some opaque data that will be passed into the callback
+            function when called. This can be used to avoid needing to do
+            complex data lookups or to keep track of what context is being
+            used. This parameter is optional.
+        """
+        helper = _OCSPServerCallbackHelper(callback)
+        self._set_ocsp_callback(helper, data)
+
+    def set_ocsp_client_callback(self, callback, data=None):
+        """
+        Set a callback to validate OCSP data stapled to the TLS handshake on
+        the client side.
+
+        :param callback: The callback function. It will be invoked with three
+            arguments: the Connection, a bytestring containing the stapled OCSP
+            assertion, and the optional arbitrary data you have provided. The
+            callback must return a boolean that indicates the result of
+            validating the OCSP data: ``True`` if the OCSP data is valid and
+            the certificate can be trusted, or ``False`` if either the OCSP
+            data is invalid or the certificate has been revoked.
+        :param data: Some opaque data that will be passed into the callback
+            function when called. This can be used to avoid needing to do
+            complex data lookups or to keep track of what context is being
+            used. This parameter is optional.
+        """
+        helper = _OCSPClientCallbackHelper(callback)
+        self._set_ocsp_callback(helper, data)
+
+    def set_cookie_generate_callback(self, callback):
+        self._cookie_generate_helper = _CookieGenerateCallbackHelper(callback)
+        _lib.SSL_CTX_set_cookie_generate_cb(
+            self._context,
+            self._cookie_generate_helper.callback,
+        )
+
+    def set_cookie_verify_callback(self, callback):
+        self._cookie_verify_helper = _CookieVerifyCallbackHelper(callback)
+        _lib.SSL_CTX_set_cookie_verify_cb(
+            self._context,
+            self._cookie_verify_helper.callback,
+        )
+
+
+class Connection(object):
+    _reverse_mapping = WeakValueDictionary()
+
+    def __init__(self, context, socket=None):
+        """
+        Create a new Connection object, using the given OpenSSL.SSL.Context
+        instance and socket.
+
+        :param context: An SSL Context to use for this connection
+        :param socket: The socket to use for transport layer
+        """
+        if not isinstance(context, Context):
+            raise TypeError("context must be a Context instance")
+
+        ssl = _lib.SSL_new(context._context)
+        self._ssl = _ffi.gc(ssl, _lib.SSL_free)
+        # We set SSL_MODE_AUTO_RETRY to handle situations where OpenSSL returns
+        # an SSL_ERROR_WANT_READ when processing a non-application data packet
+        # even though there is still data on the underlying transport.
+        # See https://github.com/openssl/openssl/issues/6234 for more details.
+        _lib.SSL_set_mode(self._ssl, _lib.SSL_MODE_AUTO_RETRY)
+        self._context = context
+        self._app_data = None
+
+        # References to strings used for Application Layer Protocol
+        # Negotiation. These strings get copied at some point but it's well
+        # after the callback returns, so we have to hang them somewhere to
+        # avoid them getting freed.
+        self._alpn_select_callback_args = None
+
+        # Reference the verify_callback of the Context. This ensures that if
+        # set_verify is called again after the SSL object has been created we
+        # do not point to a dangling reference
+        self._verify_helper = context._verify_helper
+        self._verify_callback = context._verify_callback
+
+        # And likewise for the cookie callbacks
+        self._cookie_generate_helper = context._cookie_generate_helper
+        self._cookie_verify_helper = context._cookie_verify_helper
+
+        self._reverse_mapping[self._ssl] = self
+
+        if socket is None:
+            self._socket = None
+            # Don't set up any gc for these, SSL_free will take care of them.
+            self._into_ssl = _lib.BIO_new(_lib.BIO_s_mem())
+            _openssl_assert(self._into_ssl != _ffi.NULL)
+
+            self._from_ssl = _lib.BIO_new(_lib.BIO_s_mem())
+            _openssl_assert(self._from_ssl != _ffi.NULL)
+
+            _lib.SSL_set_bio(self._ssl, self._into_ssl, self._from_ssl)
+        else:
+            self._into_ssl = None
+            self._from_ssl = None
+            self._socket = socket
+            set_result = _lib.SSL_set_fd(
+                self._ssl, _asFileDescriptor(self._socket)
+            )
+            _openssl_assert(set_result == 1)
+
+    def __getattr__(self, name):
+        """
+        Look up attributes on the wrapped socket object if they are not found
+        on the Connection object.
+        """
+        if self._socket is None:
+            raise AttributeError(
+                "'%s' object has no attribute '%s'"
+                % (self.__class__.__name__, name)
+            )
+        else:
+            return getattr(self._socket, name)
+
+    def _raise_ssl_error(self, ssl, result):
+        if self._context._verify_helper is not None:
+            self._context._verify_helper.raise_if_problem()
+        if self._context._alpn_select_helper is not None:
+            self._context._alpn_select_helper.raise_if_problem()
+        if self._context._ocsp_helper is not None:
+            self._context._ocsp_helper.raise_if_problem()
+
+        error = _lib.SSL_get_error(ssl, result)
+        if error == _lib.SSL_ERROR_WANT_READ:
+            raise WantReadError()
+        elif error == _lib.SSL_ERROR_WANT_WRITE:
+            raise WantWriteError()
+        elif error == _lib.SSL_ERROR_ZERO_RETURN:
+            raise ZeroReturnError()
+        elif error == _lib.SSL_ERROR_WANT_X509_LOOKUP:
+            # TODO: This is untested.
+            raise WantX509LookupError()
+        elif error == _lib.SSL_ERROR_SYSCALL:
+            if _lib.ERR_peek_error() == 0:
+                if result < 0:
+                    if platform == "win32":
+                        errno = _ffi.getwinerror()[0]
+                    else:
+                        errno = _ffi.errno
+
+                    if errno != 0:
+                        raise SysCallError(errno, errorcode.get(errno))
+                raise SysCallError(-1, "Unexpected EOF")
+            else:
+                # TODO: This is untested.
+                _raise_current_error()
+        elif error == _lib.SSL_ERROR_NONE:
+            pass
+        else:
+            _raise_current_error()
+
+    def get_context(self):
+        """
+        Retrieve the :class:`Context` object associated with this
+        :class:`Connection`.
+        """
+        return self._context
+
+    def set_context(self, context):
+        """
+        Switch this connection to a new session context.
+
+        :param context: A :class:`Context` instance giving the new session
+            context to use.
+        """
+        if not isinstance(context, Context):
+            raise TypeError("context must be a Context instance")
+
+        _lib.SSL_set_SSL_CTX(self._ssl, context._context)
+        self._context = context
+
+    def get_servername(self):
+        """
+        Retrieve the servername extension value if provided in the client hello
+        message, or None if there wasn't one.
+
+        :return: A byte string giving the server name or :data:`None`.
+
+        .. versionadded:: 0.13
+        """
+        name = _lib.SSL_get_servername(
+            self._ssl, _lib.TLSEXT_NAMETYPE_host_name
+        )
+        if name == _ffi.NULL:
+            return None
+
+        return _ffi.string(name)
+
+    def set_ciphertext_mtu(self, mtu):
+        """
+        For DTLS, set the maximum UDP payload size (*not* including IP/UDP
+        overhead).
+
+        Note that you might have to set :data:`OP_NO_QUERY_MTU` to prevent
+        OpenSSL from spontaneously clearing this.
+
+        :param mtu: An integer giving the maximum transmission unit.
+
+        .. versionadded:: 21.1
+        """
+        _lib.SSL_set_mtu(self._ssl, mtu)
+
+    def get_cleartext_mtu(self):
+        """
+        For DTLS, get the maximum size of unencrypted data you can pass to
+        :meth:`write` without exceeding the MTU (as passed to
+        :meth:`set_ciphertext_mtu`).
+
+        :return: The effective MTU as an integer.
+
+        .. versionadded:: 21.1
+        """
+
+        if not hasattr(_lib, "DTLS_get_data_mtu"):
+            raise NotImplementedError("requires OpenSSL 1.1.1 or better")
+        return _lib.DTLS_get_data_mtu(self._ssl)
+
+    def set_tlsext_host_name(self, name):
+        """
+        Set the value of the servername extension to send in the client hello.
+
+        :param name: A byte string giving the name.
+
+        .. versionadded:: 0.13
+        """
+        if not isinstance(name, bytes):
+            raise TypeError("name must be a byte string")
+        elif b"\0" in name:
+            raise TypeError("name must not contain NUL byte")
+
+        # XXX I guess this can fail sometimes?
+        _lib.SSL_set_tlsext_host_name(self._ssl, name)
+
+    def pending(self):
+        """
+        Get the number of bytes that can be safely read from the SSL buffer
+        (**not** the underlying transport buffer).
+
+        :return: The number of bytes available in the receive buffer.
+        """
+        return _lib.SSL_pending(self._ssl)
+
+    def send(self, buf, flags=0):
+        """
+        Send data on the connection. NOTE: If you get one of the WantRead,
+        WantWrite or WantX509Lookup exceptions on this, you have to call the
+        method again with the SAME buffer.
+
+        :param buf: The string, buffer or memoryview to send
+        :param flags: (optional) Included for compatibility with the socket
+                      API, the value is ignored
+        :return: The number of bytes written
+        """
+        # Backward compatibility
+        buf = _text_to_bytes_and_warn("buf", buf)
+
+        with _ffi.from_buffer(buf) as data:
+            # check len(buf) instead of len(data) for testability
+            if len(buf) > 2147483647:
+                raise ValueError(
+                    "Cannot send more than 2**31-1 bytes at once."
+                )
+
+            result = _lib.SSL_write(self._ssl, data, len(data))
+            self._raise_ssl_error(self._ssl, result)
+
+            return result
+
+    write = send
+
+    def sendall(self, buf, flags=0):
+        """
+        Send "all" data on the connection. This calls send() repeatedly until
+        all data is sent. If an error occurs, it's impossible to tell how much
+        data has been sent.
+
+        :param buf: The string, buffer or memoryview to send
+        :param flags: (optional) Included for compatibility with the socket
+                      API, the value is ignored
+        :return: The number of bytes written
+        """
+        buf = _text_to_bytes_and_warn("buf", buf)
+
+        with _ffi.from_buffer(buf) as data:
+
+            left_to_send = len(buf)
+            total_sent = 0
+
+            while left_to_send:
+                # SSL_write's num arg is an int,
+                # so we cannot send more than 2**31-1 bytes at once.
+                result = _lib.SSL_write(
+                    self._ssl, data + total_sent, min(left_to_send, 2147483647)
+                )
+                self._raise_ssl_error(self._ssl, result)
+                total_sent += result
+                left_to_send -= result
+
+            return total_sent
+
+    def recv(self, bufsiz, flags=None):
+        """
+        Receive data on the connection.
+
+        :param bufsiz: The maximum number of bytes to read
+        :param flags: (optional) The only supported flag is ``MSG_PEEK``,
+            all other flags are ignored.
+        :return: The string read from the Connection
+        """
+        buf = _no_zero_allocator("char[]", bufsiz)
+        if flags is not None and flags & socket.MSG_PEEK:
+            result = _lib.SSL_peek(self._ssl, buf, bufsiz)
+        else:
+            result = _lib.SSL_read(self._ssl, buf, bufsiz)
+        self._raise_ssl_error(self._ssl, result)
+        return _ffi.buffer(buf, result)[:]
+
+    read = recv
+
+    def recv_into(self, buffer, nbytes=None, flags=None):
+        """
+        Receive data on the connection and copy it directly into the provided
+        buffer, rather than creating a new string.
+
+        :param buffer: The buffer to copy into.
+        :param nbytes: (optional) The maximum number of bytes to read into the
+            buffer. If not present, defaults to the size of the buffer. If
+            larger than the size of the buffer, is reduced to the size of the
+            buffer.
+        :param flags: (optional) The only supported flag is ``MSG_PEEK``,
+            all other flags are ignored.
+        :return: The number of bytes read into the buffer.
+        """
+        if nbytes is None:
+            nbytes = len(buffer)
+        else:
+            nbytes = min(nbytes, len(buffer))
+
+        # We need to create a temporary buffer. This is annoying, it would be
+        # better if we could pass memoryviews straight into the SSL_read call,
+        # but right now we can't. Revisit this if CFFI gets that ability.
+        buf = _no_zero_allocator("char[]", nbytes)
+        if flags is not None and flags & socket.MSG_PEEK:
+            result = _lib.SSL_peek(self._ssl, buf, nbytes)
+        else:
+            result = _lib.SSL_read(self._ssl, buf, nbytes)
+        self._raise_ssl_error(self._ssl, result)
+
+        # This strange line is all to avoid a memory copy. The buffer protocol
+        # should allow us to assign a CFFI buffer to the LHS of this line, but
+        # on CPython 3.3+ that segfaults. As a workaround, we can temporarily
+        # wrap it in a memoryview.
+        buffer[:result] = memoryview(_ffi.buffer(buf, result))
+
+        return result
+
+    def _handle_bio_errors(self, bio, result):
+        if _lib.BIO_should_retry(bio):
+            if _lib.BIO_should_read(bio):
+                raise WantReadError()
+            elif _lib.BIO_should_write(bio):
+                # TODO: This is untested.
+                raise WantWriteError()
+            elif _lib.BIO_should_io_special(bio):
+                # TODO: This is untested.  I think io_special means the socket
+                # BIO has a not-yet connected socket.
+                raise ValueError("BIO_should_io_special")
+            else:
+                # TODO: This is untested.
+                raise ValueError("unknown bio failure")
+        else:
+            # TODO: This is untested.
+            _raise_current_error()
+
+    def bio_read(self, bufsiz):
+        """
+        If the Connection was created with a memory BIO, this method can be
+        used to read bytes from the write end of that memory BIO.  Many
+        Connection methods will add bytes which must be read in this manner or
+        the buffer will eventually fill up and the Connection will be able to
+        take no further actions.
+
+        :param bufsiz: The maximum number of bytes to read
+        :return: The string read.
+        """
+        if self._from_ssl is None:
+            raise TypeError("Connection sock was not None")
+
+        if not isinstance(bufsiz, int):
+            raise TypeError("bufsiz must be an integer")
+
+        buf = _no_zero_allocator("char[]", bufsiz)
+        result = _lib.BIO_read(self._from_ssl, buf, bufsiz)
+        if result <= 0:
+            self._handle_bio_errors(self._from_ssl, result)
+
+        return _ffi.buffer(buf, result)[:]
+
+    def bio_write(self, buf):
+        """
+        If the Connection was created with a memory BIO, this method can be
+        used to add bytes to the read end of that memory BIO.  The Connection
+        can then read the bytes (for example, in response to a call to
+        :meth:`recv`).
+
+        :param buf: The string to put into the memory BIO.
+        :return: The number of bytes written
+        """
+        buf = _text_to_bytes_and_warn("buf", buf)
+
+        if self._into_ssl is None:
+            raise TypeError("Connection sock was not None")
+
+        with _ffi.from_buffer(buf) as data:
+            result = _lib.BIO_write(self._into_ssl, data, len(data))
+            if result <= 0:
+                self._handle_bio_errors(self._into_ssl, result)
+            return result
+
+    def renegotiate(self):
+        """
+        Renegotiate the session.
+
+        :return: True if the renegotiation can be started, False otherwise
+        :rtype: bool
+        """
+        if not self.renegotiate_pending():
+            _openssl_assert(_lib.SSL_renegotiate(self._ssl) == 1)
+            return True
+        return False
+
+    def do_handshake(self):
+        """
+        Perform an SSL handshake (usually called after :meth:`renegotiate` or
+        one of :meth:`set_accept_state` or :meth:`set_connect_state`). This can
+        raise the same exceptions as :meth:`send` and :meth:`recv`.
+
+        :return: None.
+        """
+        result = _lib.SSL_do_handshake(self._ssl)
+        self._raise_ssl_error(self._ssl, result)
+
+    def renegotiate_pending(self):
+        """
+        Check if there's a renegotiation in progress, it will return False once
+        a renegotiation is finished.
+
+        :return: Whether there's a renegotiation in progress
+        :rtype: bool
+        """
+        return _lib.SSL_renegotiate_pending(self._ssl) == 1
+
+    def total_renegotiations(self):
+        """
+        Find out the total number of renegotiations.
+
+        :return: The number of renegotiations.
+        :rtype: int
+        """
+        return _lib.SSL_total_renegotiations(self._ssl)
+
+    def connect(self, addr):
+        """
+        Call the :meth:`connect` method of the underlying socket and set up SSL
+        on the socket, using the :class:`Context` object supplied to this
+        :class:`Connection` object at creation.
+
+        :param addr: A remote address
+        :return: What the socket's connect method returns
+        """
+        _lib.SSL_set_connect_state(self._ssl)
+        return self._socket.connect(addr)
+
+    def connect_ex(self, addr):
+        """
+        Call the :meth:`connect_ex` method of the underlying socket and set up
+        SSL on the socket, using the Context object supplied to this Connection
+        object at creation. Note that if the :meth:`connect_ex` method of the
+        socket doesn't return 0, SSL won't be initialized.
+
+        :param addr: A remove address
+        :return: What the socket's connect_ex method returns
+        """
+        connect_ex = self._socket.connect_ex
+        self.set_connect_state()
+        return connect_ex(addr)
+
+    def accept(self):
+        """
+        Call the :meth:`accept` method of the underlying socket and set up SSL
+        on the returned socket, using the Context object supplied to this
+        :class:`Connection` object at creation.
+
+        :return: A *(conn, addr)* pair where *conn* is the new
+            :class:`Connection` object created, and *address* is as returned by
+            the socket's :meth:`accept`.
+        """
+        client, addr = self._socket.accept()
+        conn = Connection(self._context, client)
+        conn.set_accept_state()
+        return (conn, addr)
+
+    def DTLSv1_listen(self):
+        """
+        Call the OpenSSL function DTLSv1_listen on this connection. See the
+        OpenSSL manual for more details.
+
+        :return: None
+        """
+        # Possible future extension: return the BIO_ADDR in some form.
+        bio_addr = _lib.BIO_ADDR_new()
+        try:
+            result = _lib.DTLSv1_listen(self._ssl, bio_addr)
+        finally:
+            _lib.BIO_ADDR_free(bio_addr)
+        # DTLSv1_listen is weird. A zero return value means 'didn't find a
+        # ClientHello with valid cookie, but keep trying'. So basically
+        # WantReadError. But it doesn't work correctly with _raise_ssl_error.
+        # So we raise it manually instead.
+        if self._cookie_generate_helper is not None:
+            self._cookie_generate_helper.raise_if_problem()
+        if self._cookie_verify_helper is not None:
+            self._cookie_verify_helper.raise_if_problem()
+        if result == 0:
+            raise WantReadError()
+        if result < 0:
+            self._raise_ssl_error(self._ssl, result)
+
+    def bio_shutdown(self):
+        """
+        If the Connection was created with a memory BIO, this method can be
+        used to indicate that *end of file* has been reached on the read end of
+        that memory BIO.
+
+        :return: None
+        """
+        if self._from_ssl is None:
+            raise TypeError("Connection sock was not None")
+
+        _lib.BIO_set_mem_eof_return(self._into_ssl, 0)
+
+    def shutdown(self):
+        """
+        Send the shutdown message to the Connection.
+
+        :return: True if the shutdown completed successfully (i.e. both sides
+                 have sent closure alerts), False otherwise (in which case you
+                 call :meth:`recv` or :meth:`send` when the connection becomes
+                 readable/writeable).
+        """
+        result = _lib.SSL_shutdown(self._ssl)
+        if result < 0:
+            self._raise_ssl_error(self._ssl, result)
+        elif result > 0:
+            return True
+        else:
+            return False
+
+    def get_cipher_list(self):
+        """
+        Retrieve the list of ciphers used by the Connection object.
+
+        :return: A list of native cipher strings.
+        """
+        ciphers = []
+        for i in count():
+            result = _lib.SSL_get_cipher_list(self._ssl, i)
+            if result == _ffi.NULL:
+                break
+            ciphers.append(_ffi.string(result).decode("utf-8"))
+        return ciphers
+
+    def get_client_ca_list(self):
+        """
+        Get CAs whose certificates are suggested for client authentication.
+
+        :return: If this is a server connection, the list of certificate
+            authorities that will be sent or has been sent to the client, as
+            controlled by this :class:`Connection`'s :class:`Context`.
+
+            If this is a client connection, the list will be empty until the
+            connection with the server is established.
+
+        .. versionadded:: 0.10
+        """
+        ca_names = _lib.SSL_get_client_CA_list(self._ssl)
+        if ca_names == _ffi.NULL:
+            # TODO: This is untested.
+            return []
+
+        result = []
+        for i in range(_lib.sk_X509_NAME_num(ca_names)):
+            name = _lib.sk_X509_NAME_value(ca_names, i)
+            copy = _lib.X509_NAME_dup(name)
+            _openssl_assert(copy != _ffi.NULL)
+
+            pyname = X509Name.__new__(X509Name)
+            pyname._name = _ffi.gc(copy, _lib.X509_NAME_free)
+            result.append(pyname)
+        return result
+
+    def makefile(self, *args, **kwargs):
+        """
+        The makefile() method is not implemented, since there is no dup
+        semantics for SSL connections
+
+        :raise: NotImplementedError
+        """
+        raise NotImplementedError(
+            "Cannot make file object of OpenSSL.SSL.Connection"
+        )
+
+    def get_app_data(self):
+        """
+        Retrieve application data as set by :meth:`set_app_data`.
+
+        :return: The application data
+        """
+        return self._app_data
+
+    def set_app_data(self, data):
+        """
+        Set application data
+
+        :param data: The application data
+        :return: None
+        """
+        self._app_data = data
+
+    def get_shutdown(self):
+        """
+        Get the shutdown state of the Connection.
+
+        :return: The shutdown state, a bitvector of SENT_SHUTDOWN,
+            RECEIVED_SHUTDOWN.
+        """
+        return _lib.SSL_get_shutdown(self._ssl)
+
+    def set_shutdown(self, state):
+        """
+        Set the shutdown state of the Connection.
+
+        :param state: bitvector of SENT_SHUTDOWN, RECEIVED_SHUTDOWN.
+        :return: None
+        """
+        if not isinstance(state, int):
+            raise TypeError("state must be an integer")
+
+        _lib.SSL_set_shutdown(self._ssl, state)
+
+    def get_state_string(self):
+        """
+        Retrieve a verbose string detailing the state of the Connection.
+
+        :return: A string representing the state
+        :rtype: bytes
+        """
+        return _ffi.string(_lib.SSL_state_string_long(self._ssl))
+
+    def server_random(self):
+        """
+        Retrieve the random value used with the server hello message.
+
+        :return: A string representing the state
+        """
+        session = _lib.SSL_get_session(self._ssl)
+        if session == _ffi.NULL:
+            return None
+        length = _lib.SSL_get_server_random(self._ssl, _ffi.NULL, 0)
+        _openssl_assert(length > 0)
+        outp = _no_zero_allocator("unsigned char[]", length)
+        _lib.SSL_get_server_random(self._ssl, outp, length)
+        return _ffi.buffer(outp, length)[:]
+
+    def client_random(self):
+        """
+        Retrieve the random value used with the client hello message.
+
+        :return: A string representing the state
+        """
+        session = _lib.SSL_get_session(self._ssl)
+        if session == _ffi.NULL:
+            return None
+
+        length = _lib.SSL_get_client_random(self._ssl, _ffi.NULL, 0)
+        _openssl_assert(length > 0)
+        outp = _no_zero_allocator("unsigned char[]", length)
+        _lib.SSL_get_client_random(self._ssl, outp, length)
+        return _ffi.buffer(outp, length)[:]
+
+    def master_key(self):
+        """
+        Retrieve the value of the master key for this session.
+
+        :return: A string representing the state
+        """
+        session = _lib.SSL_get_session(self._ssl)
+        if session == _ffi.NULL:
+            return None
+
+        length = _lib.SSL_SESSION_get_master_key(session, _ffi.NULL, 0)
+        _openssl_assert(length > 0)
+        outp = _no_zero_allocator("unsigned char[]", length)
+        _lib.SSL_SESSION_get_master_key(session, outp, length)
+        return _ffi.buffer(outp, length)[:]
+
+    def export_keying_material(self, label, olen, context=None):
+        """
+        Obtain keying material for application use.
+
+        :param: label - a disambiguating label string as described in RFC 5705
+        :param: olen - the length of the exported key material in bytes
+        :param: context - a per-association context value
+        :return: the exported key material bytes or None
+        """
+        outp = _no_zero_allocator("unsigned char[]", olen)
+        context_buf = _ffi.NULL
+        context_len = 0
+        use_context = 0
+        if context is not None:
+            context_buf = context
+            context_len = len(context)
+            use_context = 1
+        success = _lib.SSL_export_keying_material(
+            self._ssl,
+            outp,
+            olen,
+            label,
+            len(label),
+            context_buf,
+            context_len,
+            use_context,
+        )
+        _openssl_assert(success == 1)
+        return _ffi.buffer(outp, olen)[:]
+
+    def sock_shutdown(self, *args, **kwargs):
+        """
+        Call the :meth:`shutdown` method of the underlying socket.
+        See :manpage:`shutdown(2)`.
+
+        :return: What the socket's shutdown() method returns
+        """
+        return self._socket.shutdown(*args, **kwargs)
+
+    def get_certificate(self):
+        """
+        Retrieve the local certificate (if any)
+
+        :return: The local certificate
+        """
+        cert = _lib.SSL_get_certificate(self._ssl)
+        if cert != _ffi.NULL:
+            _lib.X509_up_ref(cert)
+            return X509._from_raw_x509_ptr(cert)
+        return None
+
+    def get_peer_certificate(self):
+        """
+        Retrieve the other side's certificate (if any)
+
+        :return: The peer's certificate
+        """
+        cert = _lib.SSL_get_peer_certificate(self._ssl)
+        if cert != _ffi.NULL:
+            return X509._from_raw_x509_ptr(cert)
+        return None
+
+    @staticmethod
+    def _cert_stack_to_list(cert_stack):
+        """
+        Internal helper to convert a STACK_OF(X509) to a list of X509
+        instances.
+        """
+        result = []
+        for i in range(_lib.sk_X509_num(cert_stack)):
+            cert = _lib.sk_X509_value(cert_stack, i)
+            _openssl_assert(cert != _ffi.NULL)
+            res = _lib.X509_up_ref(cert)
+            _openssl_assert(res >= 1)
+            pycert = X509._from_raw_x509_ptr(cert)
+            result.append(pycert)
+        return result
+
+    def get_peer_cert_chain(self):
+        """
+        Retrieve the other side's certificate (if any)
+
+        :return: A list of X509 instances giving the peer's certificate chain,
+                 or None if it does not have one.
+        """
+        cert_stack = _lib.SSL_get_peer_cert_chain(self._ssl)
+        if cert_stack == _ffi.NULL:
+            return None
+
+        return self._cert_stack_to_list(cert_stack)
+
+    def get_verified_chain(self):
+        """
+        Retrieve the verified certificate chain of the peer including the
+        peer's end entity certificate. It must be called after a session has
+        been successfully established. If peer verification was not successful
+        the chain may be incomplete, invalid, or None.
+
+        :return: A list of X509 instances giving the peer's verified
+                 certificate chain, or None if it does not have one.
+
+        .. versionadded:: 20.0
+        """
+        # OpenSSL 1.1+
+        cert_stack = _lib.SSL_get0_verified_chain(self._ssl)
+        if cert_stack == _ffi.NULL:
+            return None
+
+        return self._cert_stack_to_list(cert_stack)
+
+    def want_read(self):
+        """
+        Checks if more data has to be read from the transport layer to complete
+        an operation.
+
+        :return: True iff more data has to be read
+        """
+        return _lib.SSL_want_read(self._ssl)
+
+    def want_write(self):
+        """
+        Checks if there is data to write to the transport layer to complete an
+        operation.
+
+        :return: True iff there is data to write
+        """
+        return _lib.SSL_want_write(self._ssl)
+
+    def set_accept_state(self):
+        """
+        Set the connection to work in server mode. The handshake will be
+        handled automatically by read/write.
+
+        :return: None
+        """
+        _lib.SSL_set_accept_state(self._ssl)
+
+    def set_connect_state(self):
+        """
+        Set the connection to work in client mode. The handshake will be
+        handled automatically by read/write.
+
+        :return: None
+        """
+        _lib.SSL_set_connect_state(self._ssl)
+
+    def get_session(self):
+        """
+        Returns the Session currently used.
+
+        :return: An instance of :class:`OpenSSL.SSL.Session` or
+            :obj:`None` if no session exists.
+
+        .. versionadded:: 0.14
+        """
+        session = _lib.SSL_get1_session(self._ssl)
+        if session == _ffi.NULL:
+            return None
+
+        pysession = Session.__new__(Session)
+        pysession._session = _ffi.gc(session, _lib.SSL_SESSION_free)
+        return pysession
+
+    def set_session(self, session):
+        """
+        Set the session to be used when the TLS/SSL connection is established.
+
+        :param session: A Session instance representing the session to use.
+        :returns: None
+
+        .. versionadded:: 0.14
+        """
+        if not isinstance(session, Session):
+            raise TypeError("session must be a Session instance")
+
+        result = _lib.SSL_set_session(self._ssl, session._session)
+        _openssl_assert(result == 1)
+
+    def _get_finished_message(self, function):
+        """
+        Helper to implement :meth:`get_finished` and
+        :meth:`get_peer_finished`.
+
+        :param function: Either :data:`SSL_get_finished`: or
+            :data:`SSL_get_peer_finished`.
+
+        :return: :data:`None` if the desired message has not yet been
+            received, otherwise the contents of the message.
+        :rtype: :class:`bytes` or :class:`NoneType`
+        """
+        # The OpenSSL documentation says nothing about what might happen if the
+        # count argument given is zero.  Specifically, it doesn't say whether
+        # the output buffer may be NULL in that case or not.  Inspection of the
+        # implementation reveals that it calls memcpy() unconditionally.
+        # Section 7.1.4, paragraph 1 of the C standard suggests that
+        # memcpy(NULL, source, 0) is not guaranteed to produce defined (let
+        # alone desirable) behavior (though it probably does on just about
+        # every implementation...)
+        #
+        # Allocate a tiny buffer to pass in (instead of just passing NULL as
+        # one might expect) for the initial call so as to be safe against this
+        # potentially undefined behavior.
+        empty = _ffi.new("char[]", 0)
+        size = function(self._ssl, empty, 0)
+        if size == 0:
+            # No Finished message so far.
+            return None
+
+        buf = _no_zero_allocator("char[]", size)
+        function(self._ssl, buf, size)
+        return _ffi.buffer(buf, size)[:]
+
+    def get_finished(self):
+        """
+        Obtain the latest TLS Finished message that we sent.
+
+        :return: The contents of the message or :obj:`None` if the TLS
+            handshake has not yet completed.
+        :rtype: :class:`bytes` or :class:`NoneType`
+
+        .. versionadded:: 0.15
+        """
+        return self._get_finished_message(_lib.SSL_get_finished)
+
+    def get_peer_finished(self):
+        """
+        Obtain the latest TLS Finished message that we received from the peer.
+
+        :return: The contents of the message or :obj:`None` if the TLS
+            handshake has not yet completed.
+        :rtype: :class:`bytes` or :class:`NoneType`
+
+        .. versionadded:: 0.15
+        """
+        return self._get_finished_message(_lib.SSL_get_peer_finished)
+
+    def get_cipher_name(self):
+        """
+        Obtain the name of the currently used cipher.
+
+        :returns: The name of the currently used cipher or :obj:`None`
+            if no connection has been established.
+        :rtype: :class:`unicode` or :class:`NoneType`
+
+        .. versionadded:: 0.15
+        """
+        cipher = _lib.SSL_get_current_cipher(self._ssl)
+        if cipher == _ffi.NULL:
+            return None
+        else:
+            name = _ffi.string(_lib.SSL_CIPHER_get_name(cipher))
+            return name.decode("utf-8")
+
+    def get_cipher_bits(self):
+        """
+        Obtain the number of secret bits of the currently used cipher.
+
+        :returns: The number of secret bits of the currently used cipher
+            or :obj:`None` if no connection has been established.
+        :rtype: :class:`int` or :class:`NoneType`
+
+        .. versionadded:: 0.15
+        """
+        cipher = _lib.SSL_get_current_cipher(self._ssl)
+        if cipher == _ffi.NULL:
+            return None
+        else:
+            return _lib.SSL_CIPHER_get_bits(cipher, _ffi.NULL)
+
+    def get_cipher_version(self):
+        """
+        Obtain the protocol version of the currently used cipher.
+
+        :returns: The protocol name of the currently used cipher
+            or :obj:`None` if no connection has been established.
+        :rtype: :class:`unicode` or :class:`NoneType`
+
+        .. versionadded:: 0.15
+        """
+        cipher = _lib.SSL_get_current_cipher(self._ssl)
+        if cipher == _ffi.NULL:
+            return None
+        else:
+            version = _ffi.string(_lib.SSL_CIPHER_get_version(cipher))
+            return version.decode("utf-8")
+
+    def get_protocol_version_name(self):
+        """
+        Retrieve the protocol version of the current connection.
+
+        :returns: The TLS version of the current connection, for example
+            the value for TLS 1.2 would be ``TLSv1.2``or ``Unknown``
+            for connections that were not successfully established.
+        :rtype: :class:`unicode`
+        """
+        version = _ffi.string(_lib.SSL_get_version(self._ssl))
+        return version.decode("utf-8")
+
+    def get_protocol_version(self):
+        """
+        Retrieve the SSL or TLS protocol version of the current connection.
+
+        :returns: The TLS version of the current connection.  For example,
+            it will return ``0x769`` for connections made over TLS version 1.
+        :rtype: :class:`int`
+        """
+        version = _lib.SSL_version(self._ssl)
+        return version
+
+    @_requires_alpn
+    def set_alpn_protos(self, protos):
+        """
+        Specify the client's ALPN protocol list.
+
+        These protocols are offered to the server during protocol negotiation.
+
+        :param protos: A list of the protocols to be offered to the server.
+            This list should be a Python list of bytestrings representing the
+            protocols to offer, e.g. ``[b'http/1.1', b'spdy/2']``.
+        """
+        # Different versions of OpenSSL are inconsistent about how they handle
+        # empty proto lists (see #1043), so we avoid the problem entirely by
+        # rejecting them ourselves.
+        if not protos:
+            raise ValueError("at least one protocol must be specified")
+
+        # Take the list of protocols and join them together, prefixing them
+        # with their lengths.
+        protostr = b"".join(
+            chain.from_iterable((bytes((len(p),)), p) for p in protos)
+        )
+
+        # Build a C string from the list. We don't need to save this off
+        # because OpenSSL immediately copies the data out.
+        input_str = _ffi.new("unsigned char[]", protostr)
+
+        # https://www.openssl.org/docs/man1.1.0/man3/SSL_CTX_set_alpn_protos.html:
+        # SSL_CTX_set_alpn_protos() and SSL_set_alpn_protos()
+        # return 0 on success, and non-0 on failure.
+        # WARNING: these functions reverse the return value convention.
+        _openssl_assert(
+            _lib.SSL_set_alpn_protos(self._ssl, input_str, len(protostr)) == 0
+        )
+
+    @_requires_alpn
+    def get_alpn_proto_negotiated(self):
+        """
+        Get the protocol that was negotiated by ALPN.
+
+        :returns: A bytestring of the protocol name.  If no protocol has been
+            negotiated yet, returns an empty string.
+        """
+        data = _ffi.new("unsigned char **")
+        data_len = _ffi.new("unsigned int *")
+
+        _lib.SSL_get0_alpn_selected(self._ssl, data, data_len)
+
+        if not data_len:
+            return b""
+
+        return _ffi.buffer(data[0], data_len[0])[:]
+
+    def request_ocsp(self):
+        """
+        Called to request that the server sends stapled OCSP data, if
+        available. If this is not called on the client side then the server
+        will not send OCSP data. Should be used in conjunction with
+        :meth:`Context.set_ocsp_client_callback`.
+        """
+        rc = _lib.SSL_set_tlsext_status_type(
+            self._ssl, _lib.TLSEXT_STATUSTYPE_ocsp
+        )
+        _openssl_assert(rc == 1)
+
+
+# This is similar to the initialization calls at the end of OpenSSL/crypto.py
+# but is exercised mostly by the Context initializer.
+_lib.SSL_library_init()

+ 32 - 0
rest_venv/Lib/site-packages/OpenSSL/__init__.py

@@ -0,0 +1,32 @@
+# Copyright (C) AB Strakt
+# See LICENSE for details.
+
+"""
+pyOpenSSL - A simple wrapper around the OpenSSL library
+"""
+
+from OpenSSL import crypto, SSL
+from OpenSSL.version import (
+    __author__,
+    __copyright__,
+    __email__,
+    __license__,
+    __summary__,
+    __title__,
+    __uri__,
+    __version__,
+)
+
+
+__all__ = [
+    "SSL",
+    "crypto",
+    "__author__",
+    "__copyright__",
+    "__email__",
+    "__license__",
+    "__summary__",
+    "__title__",
+    "__uri__",
+    "__version__",
+]

TEMPAT SAMPAH
rest_venv/Lib/site-packages/OpenSSL/__pycache__/SSL.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/OpenSSL/__pycache__/__init__.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/OpenSSL/__pycache__/_util.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/OpenSSL/__pycache__/crypto.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/OpenSSL/__pycache__/debug.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/OpenSSL/__pycache__/rand.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/OpenSSL/__pycache__/version.cpython-39.pyc


+ 122 - 0
rest_venv/Lib/site-packages/OpenSSL/_util.py

@@ -0,0 +1,122 @@
+import os
+import sys
+import warnings
+
+from cryptography.hazmat.bindings.openssl.binding import Binding
+
+
+binding = Binding()
+ffi = binding.ffi
+lib = binding.lib
+
+
+# This is a special CFFI allocator that does not bother to zero its memory
+# after allocation. This has vastly better performance on large allocations and
+# so should be used whenever we don't need the memory zeroed out.
+no_zero_allocator = ffi.new_allocator(should_clear_after_alloc=False)
+
+
+def text(charp):
+    """
+    Get a native string type representing of the given CFFI ``char*`` object.
+
+    :param charp: A C-style string represented using CFFI.
+
+    :return: :class:`str`
+    """
+    if not charp:
+        return ""
+    return ffi.string(charp).decode("utf-8")
+
+
+def exception_from_error_queue(exception_type):
+    """
+    Convert an OpenSSL library failure into a Python exception.
+
+    When a call to the native OpenSSL library fails, this is usually signalled
+    by the return value, and an error code is stored in an error queue
+    associated with the current thread. The err library provides functions to
+    obtain these error codes and textual error messages.
+    """
+    errors = []
+
+    while True:
+        error = lib.ERR_get_error()
+        if error == 0:
+            break
+        errors.append(
+            (
+                text(lib.ERR_lib_error_string(error)),
+                text(lib.ERR_func_error_string(error)),
+                text(lib.ERR_reason_error_string(error)),
+            )
+        )
+
+    raise exception_type(errors)
+
+
+def make_assert(error):
+    """
+    Create an assert function that uses :func:`exception_from_error_queue` to
+    raise an exception wrapped by *error*.
+    """
+
+    def openssl_assert(ok):
+        """
+        If *ok* is not True, retrieve the error from OpenSSL and raise it.
+        """
+        if ok is not True:
+            exception_from_error_queue(error)
+
+    return openssl_assert
+
+
+def path_bytes(s):
+    """
+    Convert a Python path to a :py:class:`bytes` for the path which can be
+    passed into an OpenSSL API accepting a filename.
+
+    :param s: A path (valid for os.fspath).
+
+    :return: An instance of :py:class:`bytes`.
+    """
+    b = os.fspath(s)
+
+    if isinstance(b, str):
+        return b.encode(sys.getfilesystemencoding())
+    else:
+        return b
+
+
+def byte_string(s):
+    return s.encode("charmap")
+
+
+# A marker object to observe whether some optional arguments are passed any
+# value or not.
+UNSPECIFIED = object()
+
+_TEXT_WARNING = "str for {0} is no longer accepted, use bytes"
+
+
+def text_to_bytes_and_warn(label, obj):
+    """
+    If ``obj`` is text, emit a warning that it should be bytes instead and try
+    to convert it to bytes automatically.
+
+    :param str label: The name of the parameter from which ``obj`` was taken
+        (so a developer can easily find the source of the problem and correct
+        it).
+
+    :return: If ``obj`` is the text string type, a ``bytes`` object giving the
+        UTF-8 encoding of that text is returned.  Otherwise, ``obj`` itself is
+        returned.
+    """
+    if isinstance(obj, str):
+        warnings.warn(
+            _TEXT_WARNING.format(label),
+            category=DeprecationWarning,
+            stacklevel=3,
+        )
+        return obj.encode("utf-8")
+    return obj

+ 3277 - 0
rest_venv/Lib/site-packages/OpenSSL/crypto.py

@@ -0,0 +1,3277 @@
+import calendar
+import datetime
+
+from base64 import b16encode
+from functools import partial
+from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
+
+from cryptography import utils, x509
+from cryptography.hazmat.primitives.asymmetric import dsa, rsa
+
+from OpenSSL._util import (
+    ffi as _ffi,
+    lib as _lib,
+    exception_from_error_queue as _exception_from_error_queue,
+    byte_string as _byte_string,
+    path_bytes as _path_bytes,
+    UNSPECIFIED as _UNSPECIFIED,
+    text_to_bytes_and_warn as _text_to_bytes_and_warn,
+    make_assert as _make_assert,
+)
+
+__all__ = [
+    "FILETYPE_PEM",
+    "FILETYPE_ASN1",
+    "FILETYPE_TEXT",
+    "TYPE_RSA",
+    "TYPE_DSA",
+    "Error",
+    "PKey",
+    "get_elliptic_curves",
+    "get_elliptic_curve",
+    "X509Name",
+    "X509Extension",
+    "X509Req",
+    "X509",
+    "X509StoreFlags",
+    "X509Store",
+    "X509StoreContextError",
+    "X509StoreContext",
+    "load_certificate",
+    "dump_certificate",
+    "dump_publickey",
+    "dump_privatekey",
+    "Revoked",
+    "CRL",
+    "PKCS7",
+    "PKCS12",
+    "NetscapeSPKI",
+    "load_publickey",
+    "load_privatekey",
+    "dump_certificate_request",
+    "load_certificate_request",
+    "sign",
+    "verify",
+    "dump_crl",
+    "load_crl",
+    "load_pkcs7_data",
+    "load_pkcs12",
+]
+
+FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
+FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
+
+# TODO This was an API mistake.  OpenSSL has no such constant.
+FILETYPE_TEXT = 2 ** 16 - 1
+
+TYPE_RSA = _lib.EVP_PKEY_RSA
+TYPE_DSA = _lib.EVP_PKEY_DSA
+TYPE_DH = _lib.EVP_PKEY_DH
+TYPE_EC = _lib.EVP_PKEY_EC
+
+
+class Error(Exception):
+    """
+    An error occurred in an `OpenSSL.crypto` API.
+    """
+
+
+_raise_current_error = partial(_exception_from_error_queue, Error)
+_openssl_assert = _make_assert(Error)
+
+
+def _get_backend():
+    """
+    Importing the backend from cryptography has the side effect of activating
+    the osrandom engine. This mutates the global state of OpenSSL in the
+    process and causes issues for various programs that use subinterpreters or
+    embed Python. By putting the import in this function we can avoid
+    triggering this side effect unless _get_backend is called.
+    """
+    from cryptography.hazmat.backends.openssl.backend import backend
+
+    return backend
+
+
+def _untested_error(where):
+    """
+    An OpenSSL API failed somehow.  Additionally, the failure which was
+    encountered isn't one that's exercised by the test suite so future behavior
+    of pyOpenSSL is now somewhat less predictable.
+    """
+    raise RuntimeError("Unknown %s failure" % (where,))
+
+
+def _new_mem_buf(buffer=None):
+    """
+    Allocate a new OpenSSL memory BIO.
+
+    Arrange for the garbage collector to clean it up automatically.
+
+    :param buffer: None or some bytes to use to put into the BIO so that they
+        can be read out.
+    """
+    if buffer is None:
+        bio = _lib.BIO_new(_lib.BIO_s_mem())
+        free = _lib.BIO_free
+    else:
+        data = _ffi.new("char[]", buffer)
+        bio = _lib.BIO_new_mem_buf(data, len(buffer))
+
+        # Keep the memory alive as long as the bio is alive!
+        def free(bio, ref=data):
+            return _lib.BIO_free(bio)
+
+    _openssl_assert(bio != _ffi.NULL)
+
+    bio = _ffi.gc(bio, free)
+    return bio
+
+
+def _bio_to_string(bio):
+    """
+    Copy the contents of an OpenSSL BIO object into a Python byte string.
+    """
+    result_buffer = _ffi.new("char**")
+    buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
+    return _ffi.buffer(result_buffer[0], buffer_length)[:]
+
+
+def _set_asn1_time(boundary, when):
+    """
+    The the time value of an ASN1 time object.
+
+    @param boundary: An ASN1_TIME pointer (or an object safely
+        castable to that type) which will have its value set.
+    @param when: A string representation of the desired time value.
+
+    @raise TypeError: If C{when} is not a L{bytes} string.
+    @raise ValueError: If C{when} does not represent a time in the required
+        format.
+    @raise RuntimeError: If the time value cannot be set for some other
+        (unspecified) reason.
+    """
+    if not isinstance(when, bytes):
+        raise TypeError("when must be a byte string")
+
+    set_result = _lib.ASN1_TIME_set_string(boundary, when)
+    if set_result == 0:
+        raise ValueError("Invalid string")
+
+
+def _get_asn1_time(timestamp):
+    """
+    Retrieve the time value of an ASN1 time object.
+
+    @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
+        that type) from which the time value will be retrieved.
+
+    @return: The time value from C{timestamp} as a L{bytes} string in a certain
+        format.  Or C{None} if the object contains no time value.
+    """
+    string_timestamp = _ffi.cast("ASN1_STRING*", timestamp)
+    if _lib.ASN1_STRING_length(string_timestamp) == 0:
+        return None
+    elif (
+        _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME
+    ):
+        return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
+    else:
+        generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
+        _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
+        if generalized_timestamp[0] == _ffi.NULL:
+            # This may happen:
+            #   - if timestamp was not an ASN1_TIME
+            #   - if allocating memory for the ASN1_GENERALIZEDTIME failed
+            #   - if a copy of the time data from timestamp cannot be made for
+            #     the newly allocated ASN1_GENERALIZEDTIME
+            #
+            # These are difficult to test.  cffi enforces the ASN1_TIME type.
+            # Memory allocation failures are a pain to trigger
+            # deterministically.
+            _untested_error("ASN1_TIME_to_generalizedtime")
+        else:
+            string_timestamp = _ffi.cast(
+                "ASN1_STRING*", generalized_timestamp[0]
+            )
+            string_data = _lib.ASN1_STRING_data(string_timestamp)
+            string_result = _ffi.string(string_data)
+            _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
+            return string_result
+
+
+class _X509NameInvalidator(object):
+    def __init__(self):
+        self._names = []
+
+    def add(self, name):
+        self._names.append(name)
+
+    def clear(self):
+        for name in self._names:
+            # Breaks the object, but also prevents UAF!
+            del name._name
+
+
+class PKey(object):
+    """
+    A class representing an DSA or RSA public key or key pair.
+    """
+
+    _only_public = False
+    _initialized = True
+
+    def __init__(self):
+        pkey = _lib.EVP_PKEY_new()
+        self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
+        self._initialized = False
+
+    def to_cryptography_key(self):
+        """
+        Export as a ``cryptography`` key.
+
+        :rtype: One of ``cryptography``'s `key interfaces`_.
+
+        .. _key interfaces: https://cryptography.io/en/latest/hazmat/\
+            primitives/asymmetric/rsa/#key-interfaces
+
+        .. versionadded:: 16.1.0
+        """
+        from cryptography.hazmat.primitives.serialization import (
+            load_der_private_key,
+            load_der_public_key,
+        )
+
+        backend = _get_backend()
+        if self._only_public:
+            der = dump_publickey(FILETYPE_ASN1, self)
+            return load_der_public_key(der, backend)
+        else:
+            der = dump_privatekey(FILETYPE_ASN1, self)
+            return load_der_private_key(der, None, backend)
+
+    @classmethod
+    def from_cryptography_key(cls, crypto_key):
+        """
+        Construct based on a ``cryptography`` *crypto_key*.
+
+        :param crypto_key: A ``cryptography`` key.
+        :type crypto_key: One of ``cryptography``'s `key interfaces`_.
+
+        :rtype: PKey
+
+        .. versionadded:: 16.1.0
+        """
+        if not isinstance(
+            crypto_key,
+            (
+                rsa.RSAPublicKey,
+                rsa.RSAPrivateKey,
+                dsa.DSAPublicKey,
+                dsa.DSAPrivateKey,
+            ),
+        ):
+            raise TypeError("Unsupported key type")
+
+        from cryptography.hazmat.primitives.serialization import (
+            Encoding,
+            NoEncryption,
+            PrivateFormat,
+            PublicFormat,
+        )
+
+        if isinstance(crypto_key, (rsa.RSAPublicKey, dsa.DSAPublicKey)):
+            return load_publickey(
+                FILETYPE_ASN1,
+                crypto_key.public_bytes(
+                    Encoding.DER, PublicFormat.SubjectPublicKeyInfo
+                ),
+            )
+        else:
+            der = crypto_key.private_bytes(
+                Encoding.DER, PrivateFormat.PKCS8, NoEncryption()
+            )
+            return load_privatekey(FILETYPE_ASN1, der)
+
+    def generate_key(self, type, bits):
+        """
+        Generate a key pair of the given type, with the given number of bits.
+
+        This generates a key "into" the this object.
+
+        :param type: The key type.
+        :type type: :py:data:`TYPE_RSA` or :py:data:`TYPE_DSA`
+        :param bits: The number of bits.
+        :type bits: :py:data:`int` ``>= 0``
+        :raises TypeError: If :py:data:`type` or :py:data:`bits` isn't
+            of the appropriate type.
+        :raises ValueError: If the number of bits isn't an integer of
+            the appropriate size.
+        :return: ``None``
+        """
+        if not isinstance(type, int):
+            raise TypeError("type must be an integer")
+
+        if not isinstance(bits, int):
+            raise TypeError("bits must be an integer")
+
+        if type == TYPE_RSA:
+            if bits <= 0:
+                raise ValueError("Invalid number of bits")
+
+            # TODO Check error return
+            exponent = _lib.BN_new()
+            exponent = _ffi.gc(exponent, _lib.BN_free)
+            _lib.BN_set_word(exponent, _lib.RSA_F4)
+
+            rsa = _lib.RSA_new()
+
+            result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
+            _openssl_assert(result == 1)
+
+            result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
+            _openssl_assert(result == 1)
+
+        elif type == TYPE_DSA:
+            dsa = _lib.DSA_new()
+            _openssl_assert(dsa != _ffi.NULL)
+
+            dsa = _ffi.gc(dsa, _lib.DSA_free)
+            res = _lib.DSA_generate_parameters_ex(
+                dsa, bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL
+            )
+            _openssl_assert(res == 1)
+
+            _openssl_assert(_lib.DSA_generate_key(dsa) == 1)
+            _openssl_assert(_lib.EVP_PKEY_set1_DSA(self._pkey, dsa) == 1)
+        else:
+            raise Error("No such key type")
+
+        self._initialized = True
+
+    def check(self):
+        """
+        Check the consistency of an RSA private key.
+
+        This is the Python equivalent of OpenSSL's ``RSA_check_key``.
+
+        :return: ``True`` if key is consistent.
+
+        :raise OpenSSL.crypto.Error: if the key is inconsistent.
+
+        :raise TypeError: if the key is of a type which cannot be checked.
+            Only RSA keys can currently be checked.
+        """
+        if self._only_public:
+            raise TypeError("public key only")
+
+        if _lib.EVP_PKEY_type(self.type()) != _lib.EVP_PKEY_RSA:
+            raise TypeError("key type unsupported")
+
+        rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
+        rsa = _ffi.gc(rsa, _lib.RSA_free)
+        result = _lib.RSA_check_key(rsa)
+        if result == 1:
+            return True
+        _raise_current_error()
+
+    def type(self):
+        """
+        Returns the type of the key
+
+        :return: The type of the key.
+        """
+        return _lib.EVP_PKEY_id(self._pkey)
+
+    def bits(self):
+        """
+        Returns the number of bits of the key
+
+        :return: The number of bits of the key.
+        """
+        return _lib.EVP_PKEY_bits(self._pkey)
+
+
+class _EllipticCurve(object):
+    """
+    A representation of a supported elliptic curve.
+
+    @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
+        Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
+        instances each of which represents one curve supported by the system.
+    @type _curves: :py:type:`NoneType` or :py:type:`set`
+    """
+
+    _curves = None
+
+    def __ne__(self, other):
+        """
+        Implement cooperation with the right-hand side argument of ``!=``.
+
+        Python 3 seems to have dropped this cooperation in this very narrow
+        circumstance.
+        """
+        if isinstance(other, _EllipticCurve):
+            return super(_EllipticCurve, self).__ne__(other)
+        return NotImplemented
+
+    @classmethod
+    def _load_elliptic_curves(cls, lib):
+        """
+        Get the curves supported by OpenSSL.
+
+        :param lib: The OpenSSL library binding object.
+
+        :return: A :py:type:`set` of ``cls`` instances giving the names of the
+            elliptic curves the underlying library supports.
+        """
+        num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
+        builtin_curves = _ffi.new("EC_builtin_curve[]", num_curves)
+        # The return value on this call should be num_curves again.  We
+        # could check it to make sure but if it *isn't* then.. what could
+        # we do? Abort the whole process, I suppose...?  -exarkun
+        lib.EC_get_builtin_curves(builtin_curves, num_curves)
+        return set(cls.from_nid(lib, c.nid) for c in builtin_curves)
+
+    @classmethod
+    def _get_elliptic_curves(cls, lib):
+        """
+        Get, cache, and return the curves supported by OpenSSL.
+
+        :param lib: The OpenSSL library binding object.
+
+        :return: A :py:type:`set` of ``cls`` instances giving the names of the
+            elliptic curves the underlying library supports.
+        """
+        if cls._curves is None:
+            cls._curves = cls._load_elliptic_curves(lib)
+        return cls._curves
+
+    @classmethod
+    def from_nid(cls, lib, nid):
+        """
+        Instantiate a new :py:class:`_EllipticCurve` associated with the given
+        OpenSSL NID.
+
+        :param lib: The OpenSSL library binding object.
+
+        :param nid: The OpenSSL NID the resulting curve object will represent.
+            This must be a curve NID (and not, for example, a hash NID) or
+            subsequent operations will fail in unpredictable ways.
+        :type nid: :py:class:`int`
+
+        :return: The curve object.
+        """
+        return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
+
+    def __init__(self, lib, nid, name):
+        """
+        :param _lib: The :py:mod:`cryptography` binding instance used to
+            interface with OpenSSL.
+
+        :param _nid: The OpenSSL NID identifying the curve this object
+            represents.
+        :type _nid: :py:class:`int`
+
+        :param name: The OpenSSL short name identifying the curve this object
+            represents.
+        :type name: :py:class:`unicode`
+        """
+        self._lib = lib
+        self._nid = nid
+        self.name = name
+
+    def __repr__(self):
+        return "<Curve %r>" % (self.name,)
+
+    def _to_EC_KEY(self):
+        """
+        Create a new OpenSSL EC_KEY structure initialized to use this curve.
+
+        The structure is automatically garbage collected when the Python object
+        is garbage collected.
+        """
+        key = self._lib.EC_KEY_new_by_curve_name(self._nid)
+        return _ffi.gc(key, _lib.EC_KEY_free)
+
+
+def get_elliptic_curves():
+    """
+    Return a set of objects representing the elliptic curves supported in the
+    OpenSSL build in use.
+
+    The curve objects have a :py:class:`unicode` ``name`` attribute by which
+    they identify themselves.
+
+    The curve objects are useful as values for the argument accepted by
+    :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
+    used for ECDHE key exchange.
+    """
+    return _EllipticCurve._get_elliptic_curves(_lib)
+
+
+def get_elliptic_curve(name):
+    """
+    Return a single curve object selected by name.
+
+    See :py:func:`get_elliptic_curves` for information about curve objects.
+
+    :param name: The OpenSSL short name identifying the curve object to
+        retrieve.
+    :type name: :py:class:`unicode`
+
+    If the named curve is not supported then :py:class:`ValueError` is raised.
+    """
+    for curve in get_elliptic_curves():
+        if curve.name == name:
+            return curve
+    raise ValueError("unknown curve name", name)
+
+
+class X509Name(object):
+    """
+    An X.509 Distinguished Name.
+
+    :ivar countryName: The country of the entity.
+    :ivar C: Alias for  :py:attr:`countryName`.
+
+    :ivar stateOrProvinceName: The state or province of the entity.
+    :ivar ST: Alias for :py:attr:`stateOrProvinceName`.
+
+    :ivar localityName: The locality of the entity.
+    :ivar L: Alias for :py:attr:`localityName`.
+
+    :ivar organizationName: The organization name of the entity.
+    :ivar O: Alias for :py:attr:`organizationName`.
+
+    :ivar organizationalUnitName: The organizational unit of the entity.
+    :ivar OU: Alias for :py:attr:`organizationalUnitName`
+
+    :ivar commonName: The common name of the entity.
+    :ivar CN: Alias for :py:attr:`commonName`.
+
+    :ivar emailAddress: The e-mail address of the entity.
+    """
+
+    def __init__(self, name):
+        """
+        Create a new X509Name, copying the given X509Name instance.
+
+        :param name: The name to copy.
+        :type name: :py:class:`X509Name`
+        """
+        name = _lib.X509_NAME_dup(name._name)
+        self._name = _ffi.gc(name, _lib.X509_NAME_free)
+
+    def __setattr__(self, name, value):
+        if name.startswith("_"):
+            return super(X509Name, self).__setattr__(name, value)
+
+        # Note: we really do not want str subclasses here, so we do not use
+        # isinstance.
+        if type(name) is not str:
+            raise TypeError(
+                "attribute name must be string, not '%.200s'"
+                % (type(value).__name__,)
+            )
+
+        nid = _lib.OBJ_txt2nid(_byte_string(name))
+        if nid == _lib.NID_undef:
+            try:
+                _raise_current_error()
+            except Error:
+                pass
+            raise AttributeError("No such attribute")
+
+        # If there's an old entry for this NID, remove it
+        for i in range(_lib.X509_NAME_entry_count(self._name)):
+            ent = _lib.X509_NAME_get_entry(self._name, i)
+            ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
+            ent_nid = _lib.OBJ_obj2nid(ent_obj)
+            if nid == ent_nid:
+                ent = _lib.X509_NAME_delete_entry(self._name, i)
+                _lib.X509_NAME_ENTRY_free(ent)
+                break
+
+        if isinstance(value, str):
+            value = value.encode("utf-8")
+
+        add_result = _lib.X509_NAME_add_entry_by_NID(
+            self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0
+        )
+        if not add_result:
+            _raise_current_error()
+
+    def __getattr__(self, name):
+        """
+        Find attribute. An X509Name object has the following attributes:
+        countryName (alias C), stateOrProvince (alias ST), locality (alias L),
+        organization (alias O), organizationalUnit (alias OU), commonName
+        (alias CN) and more...
+        """
+        nid = _lib.OBJ_txt2nid(_byte_string(name))
+        if nid == _lib.NID_undef:
+            # This is a bit weird.  OBJ_txt2nid indicated failure, but it seems
+            # a lower level function, a2d_ASN1_OBJECT, also feels the need to
+            # push something onto the error queue.  If we don't clean that up
+            # now, someone else will bump into it later and be quite confused.
+            # See lp#314814.
+            try:
+                _raise_current_error()
+            except Error:
+                pass
+            return super(X509Name, self).__getattr__(name)
+
+        entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
+        if entry_index == -1:
+            return None
+
+        entry = _lib.X509_NAME_get_entry(self._name, entry_index)
+        data = _lib.X509_NAME_ENTRY_get_data(entry)
+
+        result_buffer = _ffi.new("unsigned char**")
+        data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
+        _openssl_assert(data_length >= 0)
+
+        try:
+            result = _ffi.buffer(result_buffer[0], data_length)[:].decode(
+                "utf-8"
+            )
+        finally:
+            # XXX untested
+            _lib.OPENSSL_free(result_buffer[0])
+        return result
+
+    def _cmp(op):
+        def f(self, other):
+            if not isinstance(other, X509Name):
+                return NotImplemented
+            result = _lib.X509_NAME_cmp(self._name, other._name)
+            return op(result, 0)
+
+        return f
+
+    __eq__ = _cmp(__eq__)
+    __ne__ = _cmp(__ne__)
+
+    __lt__ = _cmp(__lt__)
+    __le__ = _cmp(__le__)
+
+    __gt__ = _cmp(__gt__)
+    __ge__ = _cmp(__ge__)
+
+    def __repr__(self):
+        """
+        String representation of an X509Name
+        """
+        result_buffer = _ffi.new("char[]", 512)
+        format_result = _lib.X509_NAME_oneline(
+            self._name, result_buffer, len(result_buffer)
+        )
+        _openssl_assert(format_result != _ffi.NULL)
+
+        return "<X509Name object '%s'>" % (
+            _ffi.string(result_buffer).decode("utf-8"),
+        )
+
+    def hash(self):
+        """
+        Return an integer representation of the first four bytes of the
+        MD5 digest of the DER representation of the name.
+
+        This is the Python equivalent of OpenSSL's ``X509_NAME_hash``.
+
+        :return: The (integer) hash of this name.
+        :rtype: :py:class:`int`
+        """
+        return _lib.X509_NAME_hash(self._name)
+
+    def der(self):
+        """
+        Return the DER encoding of this name.
+
+        :return: The DER encoded form of this name.
+        :rtype: :py:class:`bytes`
+        """
+        result_buffer = _ffi.new("unsigned char**")
+        encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
+        _openssl_assert(encode_result >= 0)
+
+        string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
+        _lib.OPENSSL_free(result_buffer[0])
+        return string_result
+
+    def get_components(self):
+        """
+        Returns the components of this name, as a sequence of 2-tuples.
+
+        :return: The components of this name.
+        :rtype: :py:class:`list` of ``name, value`` tuples.
+        """
+        result = []
+        for i in range(_lib.X509_NAME_entry_count(self._name)):
+            ent = _lib.X509_NAME_get_entry(self._name, i)
+
+            fname = _lib.X509_NAME_ENTRY_get_object(ent)
+            fval = _lib.X509_NAME_ENTRY_get_data(ent)
+
+            nid = _lib.OBJ_obj2nid(fname)
+            name = _lib.OBJ_nid2sn(nid)
+
+            # ffi.string does not handle strings containing NULL bytes
+            # (which may have been generated by old, broken software)
+            value = _ffi.buffer(
+                _lib.ASN1_STRING_data(fval), _lib.ASN1_STRING_length(fval)
+            )[:]
+            result.append((_ffi.string(name), value))
+
+        return result
+
+
+class X509Extension(object):
+    """
+    An X.509 v3 certificate extension.
+    """
+
+    def __init__(self, type_name, critical, value, subject=None, issuer=None):
+        """
+        Initializes an X509 extension.
+
+        :param type_name: The name of the type of extension_ to create.
+        :type type_name: :py:data:`bytes`
+
+        :param bool critical: A flag indicating whether this is a critical
+            extension.
+
+        :param value: The value of the extension.
+        :type value: :py:data:`bytes`
+
+        :param subject: Optional X509 certificate to use as subject.
+        :type subject: :py:class:`X509`
+
+        :param issuer: Optional X509 certificate to use as issuer.
+        :type issuer: :py:class:`X509`
+
+        .. _extension: https://www.openssl.org/docs/manmaster/man5/
+            x509v3_config.html#STANDARD-EXTENSIONS
+        """
+        ctx = _ffi.new("X509V3_CTX*")
+
+        # A context is necessary for any extension which uses the r2i
+        # conversion method.  That is, X509V3_EXT_nconf may segfault if passed
+        # a NULL ctx. Start off by initializing most of the fields to NULL.
+        _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
+
+        # We have no configuration database - but perhaps we should (some
+        # extensions may require it).
+        _lib.X509V3_set_ctx_nodb(ctx)
+
+        # Initialize the subject and issuer, if appropriate.  ctx is a local,
+        # and as far as I can tell none of the X509V3_* APIs invoked here steal
+        # any references, so no need to mess with reference counts or
+        # duplicates.
+        if issuer is not None:
+            if not isinstance(issuer, X509):
+                raise TypeError("issuer must be an X509 instance")
+            ctx.issuer_cert = issuer._x509
+        if subject is not None:
+            if not isinstance(subject, X509):
+                raise TypeError("subject must be an X509 instance")
+            ctx.subject_cert = subject._x509
+
+        if critical:
+            # There are other OpenSSL APIs which would let us pass in critical
+            # separately, but they're harder to use, and since value is already
+            # a pile of crappy junk smuggling a ton of utterly important
+            # structured data, what's the point of trying to avoid nasty stuff
+            # with strings? (However, X509V3_EXT_i2d in particular seems like
+            # it would be a better API to invoke.  I do not know where to get
+            # the ext_struc it desires for its last parameter, though.)
+            value = b"critical," + value
+
+        extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
+        if extension == _ffi.NULL:
+            _raise_current_error()
+        self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
+
+    @property
+    def _nid(self):
+        return _lib.OBJ_obj2nid(
+            _lib.X509_EXTENSION_get_object(self._extension)
+        )
+
+    _prefixes = {
+        _lib.GEN_EMAIL: "email",
+        _lib.GEN_DNS: "DNS",
+        _lib.GEN_URI: "URI",
+    }
+
+    def _subjectAltNameString(self):
+        names = _ffi.cast(
+            "GENERAL_NAMES*", _lib.X509V3_EXT_d2i(self._extension)
+        )
+
+        names = _ffi.gc(names, _lib.GENERAL_NAMES_free)
+        parts = []
+        for i in range(_lib.sk_GENERAL_NAME_num(names)):
+            name = _lib.sk_GENERAL_NAME_value(names, i)
+            try:
+                label = self._prefixes[name.type]
+            except KeyError:
+                bio = _new_mem_buf()
+                _lib.GENERAL_NAME_print(bio, name)
+                parts.append(_bio_to_string(bio).decode("utf-8"))
+            else:
+                value = _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[
+                    :
+                ].decode("utf-8")
+                parts.append(label + ":" + value)
+        return ", ".join(parts)
+
+    def __str__(self):
+        """
+        :return: a nice text representation of the extension
+        """
+        if _lib.NID_subject_alt_name == self._nid:
+            return self._subjectAltNameString()
+
+        bio = _new_mem_buf()
+        print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
+        _openssl_assert(print_result != 0)
+
+        return _bio_to_string(bio).decode("utf-8")
+
+    def get_critical(self):
+        """
+        Returns the critical field of this X.509 extension.
+
+        :return: The critical field.
+        """
+        return _lib.X509_EXTENSION_get_critical(self._extension)
+
+    def get_short_name(self):
+        """
+        Returns the short type name of this X.509 extension.
+
+        The result is a byte string such as :py:const:`b"basicConstraints"`.
+
+        :return: The short type name.
+        :rtype: :py:data:`bytes`
+
+        .. versionadded:: 0.12
+        """
+        obj = _lib.X509_EXTENSION_get_object(self._extension)
+        nid = _lib.OBJ_obj2nid(obj)
+        return _ffi.string(_lib.OBJ_nid2sn(nid))
+
+    def get_data(self):
+        """
+        Returns the data of the X509 extension, encoded as ASN.1.
+
+        :return: The ASN.1 encoded data of this X509 extension.
+        :rtype: :py:data:`bytes`
+
+        .. versionadded:: 0.12
+        """
+        octet_result = _lib.X509_EXTENSION_get_data(self._extension)
+        string_result = _ffi.cast("ASN1_STRING*", octet_result)
+        char_result = _lib.ASN1_STRING_data(string_result)
+        result_length = _lib.ASN1_STRING_length(string_result)
+        return _ffi.buffer(char_result, result_length)[:]
+
+
+class X509Req(object):
+    """
+    An X.509 certificate signing requests.
+    """
+
+    def __init__(self):
+        req = _lib.X509_REQ_new()
+        self._req = _ffi.gc(req, _lib.X509_REQ_free)
+        # Default to version 0.
+        self.set_version(0)
+
+    def to_cryptography(self):
+        """
+        Export as a ``cryptography`` certificate signing request.
+
+        :rtype: ``cryptography.x509.CertificateSigningRequest``
+
+        .. versionadded:: 17.1.0
+        """
+        from cryptography.x509 import load_der_x509_csr
+
+        der = dump_certificate_request(FILETYPE_ASN1, self)
+
+        backend = _get_backend()
+        return load_der_x509_csr(der, backend)
+
+    @classmethod
+    def from_cryptography(cls, crypto_req):
+        """
+        Construct based on a ``cryptography`` *crypto_req*.
+
+        :param crypto_req: A ``cryptography`` X.509 certificate signing request
+        :type crypto_req: ``cryptography.x509.CertificateSigningRequest``
+
+        :rtype: X509Req
+
+        .. versionadded:: 17.1.0
+        """
+        if not isinstance(crypto_req, x509.CertificateSigningRequest):
+            raise TypeError("Must be a certificate signing request")
+
+        from cryptography.hazmat.primitives.serialization import Encoding
+
+        der = crypto_req.public_bytes(Encoding.DER)
+        return load_certificate_request(FILETYPE_ASN1, der)
+
+    def set_pubkey(self, pkey):
+        """
+        Set the public key of the certificate signing request.
+
+        :param pkey: The public key to use.
+        :type pkey: :py:class:`PKey`
+
+        :return: ``None``
+        """
+        set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
+        _openssl_assert(set_result == 1)
+
+    def get_pubkey(self):
+        """
+        Get the public key of the certificate signing request.
+
+        :return: The public key.
+        :rtype: :py:class:`PKey`
+        """
+        pkey = PKey.__new__(PKey)
+        pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
+        _openssl_assert(pkey._pkey != _ffi.NULL)
+        pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
+        pkey._only_public = True
+        return pkey
+
+    def set_version(self, version):
+        """
+        Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
+        request.
+
+        :param int version: The version number.
+        :return: ``None``
+        """
+        set_result = _lib.X509_REQ_set_version(self._req, version)
+        _openssl_assert(set_result == 1)
+
+    def get_version(self):
+        """
+        Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
+        request.
+
+        :return: The value of the version subfield.
+        :rtype: :py:class:`int`
+        """
+        return _lib.X509_REQ_get_version(self._req)
+
+    def get_subject(self):
+        """
+        Return the subject of this certificate signing request.
+
+        This creates a new :class:`X509Name` that wraps the underlying subject
+        name field on the certificate signing request. Modifying it will modify
+        the underlying signing request, and will have the effect of modifying
+        any other :class:`X509Name` that refers to this subject.
+
+        :return: The subject of this certificate signing request.
+        :rtype: :class:`X509Name`
+        """
+        name = X509Name.__new__(X509Name)
+        name._name = _lib.X509_REQ_get_subject_name(self._req)
+        _openssl_assert(name._name != _ffi.NULL)
+
+        # The name is owned by the X509Req structure.  As long as the X509Name
+        # Python object is alive, keep the X509Req Python object alive.
+        name._owner = self
+
+        return name
+
+    def add_extensions(self, extensions):
+        """
+        Add extensions to the certificate signing request.
+
+        :param extensions: The X.509 extensions to add.
+        :type extensions: iterable of :py:class:`X509Extension`
+        :return: ``None``
+        """
+        stack = _lib.sk_X509_EXTENSION_new_null()
+        _openssl_assert(stack != _ffi.NULL)
+
+        stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
+
+        for ext in extensions:
+            if not isinstance(ext, X509Extension):
+                raise ValueError("One of the elements is not an X509Extension")
+
+            # TODO push can fail (here and elsewhere)
+            _lib.sk_X509_EXTENSION_push(stack, ext._extension)
+
+        add_result = _lib.X509_REQ_add_extensions(self._req, stack)
+        _openssl_assert(add_result == 1)
+
+    def get_extensions(self):
+        """
+        Get X.509 extensions in the certificate signing request.
+
+        :return: The X.509 extensions in this request.
+        :rtype: :py:class:`list` of :py:class:`X509Extension` objects.
+
+        .. versionadded:: 0.15
+        """
+        exts = []
+        native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
+        native_exts_obj = _ffi.gc(
+            native_exts_obj,
+            lambda x: _lib.sk_X509_EXTENSION_pop_free(
+                x,
+                _ffi.addressof(_lib._original_lib, "X509_EXTENSION_free"),
+            ),
+        )
+
+        for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
+            ext = X509Extension.__new__(X509Extension)
+            extension = _lib.X509_EXTENSION_dup(
+                _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
+            )
+            ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
+            exts.append(ext)
+        return exts
+
+    def sign(self, pkey, digest):
+        """
+        Sign the certificate signing request with this key and digest type.
+
+        :param pkey: The key pair to sign with.
+        :type pkey: :py:class:`PKey`
+        :param digest: The name of the message digest to use for the signature,
+            e.g. :py:data:`"sha256"`.
+        :type digest: :py:class:`str`
+        :return: ``None``
+        """
+        if pkey._only_public:
+            raise ValueError("Key has only public part")
+
+        if not pkey._initialized:
+            raise ValueError("Key is uninitialized")
+
+        digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
+        if digest_obj == _ffi.NULL:
+            raise ValueError("No such digest method")
+
+        sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
+        _openssl_assert(sign_result > 0)
+
+    def verify(self, pkey):
+        """
+        Verifies the signature on this certificate signing request.
+
+        :param PKey key: A public key.
+
+        :return: ``True`` if the signature is correct.
+        :rtype: bool
+
+        :raises OpenSSL.crypto.Error: If the signature is invalid or there is a
+            problem verifying the signature.
+        """
+        if not isinstance(pkey, PKey):
+            raise TypeError("pkey must be a PKey instance")
+
+        result = _lib.X509_REQ_verify(self._req, pkey._pkey)
+        if result <= 0:
+            _raise_current_error()
+
+        return result
+
+
+class X509(object):
+    """
+    An X.509 certificate.
+    """
+
+    def __init__(self):
+        x509 = _lib.X509_new()
+        _openssl_assert(x509 != _ffi.NULL)
+        self._x509 = _ffi.gc(x509, _lib.X509_free)
+
+        self._issuer_invalidator = _X509NameInvalidator()
+        self._subject_invalidator = _X509NameInvalidator()
+
+    @classmethod
+    def _from_raw_x509_ptr(cls, x509):
+        cert = cls.__new__(cls)
+        cert._x509 = _ffi.gc(x509, _lib.X509_free)
+        cert._issuer_invalidator = _X509NameInvalidator()
+        cert._subject_invalidator = _X509NameInvalidator()
+        return cert
+
+    def to_cryptography(self):
+        """
+        Export as a ``cryptography`` certificate.
+
+        :rtype: ``cryptography.x509.Certificate``
+
+        .. versionadded:: 17.1.0
+        """
+        from cryptography.x509 import load_der_x509_certificate
+
+        der = dump_certificate(FILETYPE_ASN1, self)
+        backend = _get_backend()
+        return load_der_x509_certificate(der, backend)
+
+    @classmethod
+    def from_cryptography(cls, crypto_cert):
+        """
+        Construct based on a ``cryptography`` *crypto_cert*.
+
+        :param crypto_key: A ``cryptography`` X.509 certificate.
+        :type crypto_key: ``cryptography.x509.Certificate``
+
+        :rtype: X509
+
+        .. versionadded:: 17.1.0
+        """
+        if not isinstance(crypto_cert, x509.Certificate):
+            raise TypeError("Must be a certificate")
+
+        from cryptography.hazmat.primitives.serialization import Encoding
+
+        der = crypto_cert.public_bytes(Encoding.DER)
+        return load_certificate(FILETYPE_ASN1, der)
+
+    def set_version(self, version):
+        """
+        Set the version number of the certificate. Note that the
+        version value is zero-based, eg. a value of 0 is V1.
+
+        :param version: The version number of the certificate.
+        :type version: :py:class:`int`
+
+        :return: ``None``
+        """
+        if not isinstance(version, int):
+            raise TypeError("version must be an integer")
+
+        _lib.X509_set_version(self._x509, version)
+
+    def get_version(self):
+        """
+        Return the version number of the certificate.
+
+        :return: The version number of the certificate.
+        :rtype: :py:class:`int`
+        """
+        return _lib.X509_get_version(self._x509)
+
+    def get_pubkey(self):
+        """
+        Get the public key of the certificate.
+
+        :return: The public key.
+        :rtype: :py:class:`PKey`
+        """
+        pkey = PKey.__new__(PKey)
+        pkey._pkey = _lib.X509_get_pubkey(self._x509)
+        if pkey._pkey == _ffi.NULL:
+            _raise_current_error()
+        pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
+        pkey._only_public = True
+        return pkey
+
+    def set_pubkey(self, pkey):
+        """
+        Set the public key of the certificate.
+
+        :param pkey: The public key.
+        :type pkey: :py:class:`PKey`
+
+        :return: :py:data:`None`
+        """
+        if not isinstance(pkey, PKey):
+            raise TypeError("pkey must be a PKey instance")
+
+        set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
+        _openssl_assert(set_result == 1)
+
+    def sign(self, pkey, digest):
+        """
+        Sign the certificate with this key and digest type.
+
+        :param pkey: The key to sign with.
+        :type pkey: :py:class:`PKey`
+
+        :param digest: The name of the message digest to use.
+        :type digest: :py:class:`str`
+
+        :return: :py:data:`None`
+        """
+        if not isinstance(pkey, PKey):
+            raise TypeError("pkey must be a PKey instance")
+
+        if pkey._only_public:
+            raise ValueError("Key only has public part")
+
+        if not pkey._initialized:
+            raise ValueError("Key is uninitialized")
+
+        evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
+        if evp_md == _ffi.NULL:
+            raise ValueError("No such digest method")
+
+        sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
+        _openssl_assert(sign_result > 0)
+
+    def get_signature_algorithm(self):
+        """
+        Return the signature algorithm used in the certificate.
+
+        :return: The name of the algorithm.
+        :rtype: :py:class:`bytes`
+
+        :raises ValueError: If the signature algorithm is undefined.
+
+        .. versionadded:: 0.13
+        """
+        algor = _lib.X509_get0_tbs_sigalg(self._x509)
+        nid = _lib.OBJ_obj2nid(algor.algorithm)
+        if nid == _lib.NID_undef:
+            raise ValueError("Undefined signature algorithm")
+        return _ffi.string(_lib.OBJ_nid2ln(nid))
+
+    def digest(self, digest_name):
+        """
+        Return the digest of the X509 object.
+
+        :param digest_name: The name of the digest algorithm to use.
+        :type digest_name: :py:class:`str`
+
+        :return: The digest of the object, formatted as
+            :py:const:`b":"`-delimited hex pairs.
+        :rtype: :py:class:`bytes`
+        """
+        digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
+        if digest == _ffi.NULL:
+            raise ValueError("No such digest method")
+
+        result_buffer = _ffi.new("unsigned char[]", _lib.EVP_MAX_MD_SIZE)
+        result_length = _ffi.new("unsigned int[]", 1)
+        result_length[0] = len(result_buffer)
+
+        digest_result = _lib.X509_digest(
+            self._x509, digest, result_buffer, result_length
+        )
+        _openssl_assert(digest_result == 1)
+
+        return b":".join(
+            [
+                b16encode(ch).upper()
+                for ch in _ffi.buffer(result_buffer, result_length[0])
+            ]
+        )
+
+    def subject_name_hash(self):
+        """
+        Return the hash of the X509 subject.
+
+        :return: The hash of the subject.
+        :rtype: :py:class:`bytes`
+        """
+        return _lib.X509_subject_name_hash(self._x509)
+
+    def set_serial_number(self, serial):
+        """
+        Set the serial number of the certificate.
+
+        :param serial: The new serial number.
+        :type serial: :py:class:`int`
+
+        :return: :py:data`None`
+        """
+        if not isinstance(serial, int):
+            raise TypeError("serial must be an integer")
+
+        hex_serial = hex(serial)[2:]
+        if not isinstance(hex_serial, bytes):
+            hex_serial = hex_serial.encode("ascii")
+
+        bignum_serial = _ffi.new("BIGNUM**")
+
+        # BN_hex2bn stores the result in &bignum.  Unless it doesn't feel like
+        # it.  If bignum is still NULL after this call, then the return value
+        # is actually the result.  I hope.  -exarkun
+        small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
+
+        if bignum_serial[0] == _ffi.NULL:
+            set_result = _lib.ASN1_INTEGER_set(
+                _lib.X509_get_serialNumber(self._x509), small_serial
+            )
+            if set_result:
+                # TODO Not tested
+                _raise_current_error()
+        else:
+            asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
+            _lib.BN_free(bignum_serial[0])
+            if asn1_serial == _ffi.NULL:
+                # TODO Not tested
+                _raise_current_error()
+            asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
+            set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
+            _openssl_assert(set_result == 1)
+
+    def get_serial_number(self):
+        """
+        Return the serial number of this certificate.
+
+        :return: The serial number.
+        :rtype: int
+        """
+        asn1_serial = _lib.X509_get_serialNumber(self._x509)
+        bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
+        try:
+            hex_serial = _lib.BN_bn2hex(bignum_serial)
+            try:
+                hexstring_serial = _ffi.string(hex_serial)
+                serial = int(hexstring_serial, 16)
+                return serial
+            finally:
+                _lib.OPENSSL_free(hex_serial)
+        finally:
+            _lib.BN_free(bignum_serial)
+
+    def gmtime_adj_notAfter(self, amount):
+        """
+        Adjust the time stamp on which the certificate stops being valid.
+
+        :param int amount: The number of seconds by which to adjust the
+            timestamp.
+        :return: ``None``
+        """
+        if not isinstance(amount, int):
+            raise TypeError("amount must be an integer")
+
+        notAfter = _lib.X509_getm_notAfter(self._x509)
+        _lib.X509_gmtime_adj(notAfter, amount)
+
+    def gmtime_adj_notBefore(self, amount):
+        """
+        Adjust the timestamp on which the certificate starts being valid.
+
+        :param amount: The number of seconds by which to adjust the timestamp.
+        :return: ``None``
+        """
+        if not isinstance(amount, int):
+            raise TypeError("amount must be an integer")
+
+        notBefore = _lib.X509_getm_notBefore(self._x509)
+        _lib.X509_gmtime_adj(notBefore, amount)
+
+    def has_expired(self):
+        """
+        Check whether the certificate has expired.
+
+        :return: ``True`` if the certificate has expired, ``False`` otherwise.
+        :rtype: bool
+        """
+        time_string = self.get_notAfter().decode("utf-8")
+        not_after = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")
+
+        return not_after < datetime.datetime.utcnow()
+
+    def _get_boundary_time(self, which):
+        return _get_asn1_time(which(self._x509))
+
+    def get_notBefore(self):
+        """
+        Get the timestamp at which the certificate starts being valid.
+
+        The timestamp is formatted as an ASN.1 TIME::
+
+            YYYYMMDDhhmmssZ
+
+        :return: A timestamp string, or ``None`` if there is none.
+        :rtype: bytes or NoneType
+        """
+        return self._get_boundary_time(_lib.X509_getm_notBefore)
+
+    def _set_boundary_time(self, which, when):
+        return _set_asn1_time(which(self._x509), when)
+
+    def set_notBefore(self, when):
+        """
+        Set the timestamp at which the certificate starts being valid.
+
+        The timestamp is formatted as an ASN.1 TIME::
+
+            YYYYMMDDhhmmssZ
+
+        :param bytes when: A timestamp string.
+        :return: ``None``
+        """
+        return self._set_boundary_time(_lib.X509_getm_notBefore, when)
+
+    def get_notAfter(self):
+        """
+        Get the timestamp at which the certificate stops being valid.
+
+        The timestamp is formatted as an ASN.1 TIME::
+
+            YYYYMMDDhhmmssZ
+
+        :return: A timestamp string, or ``None`` if there is none.
+        :rtype: bytes or NoneType
+        """
+        return self._get_boundary_time(_lib.X509_getm_notAfter)
+
+    def set_notAfter(self, when):
+        """
+        Set the timestamp at which the certificate stops being valid.
+
+        The timestamp is formatted as an ASN.1 TIME::
+
+            YYYYMMDDhhmmssZ
+
+        :param bytes when: A timestamp string.
+        :return: ``None``
+        """
+        return self._set_boundary_time(_lib.X509_getm_notAfter, when)
+
+    def _get_name(self, which):
+        name = X509Name.__new__(X509Name)
+        name._name = which(self._x509)
+        _openssl_assert(name._name != _ffi.NULL)
+
+        # The name is owned by the X509 structure.  As long as the X509Name
+        # Python object is alive, keep the X509 Python object alive.
+        name._owner = self
+
+        return name
+
+    def _set_name(self, which, name):
+        if not isinstance(name, X509Name):
+            raise TypeError("name must be an X509Name")
+        set_result = which(self._x509, name._name)
+        _openssl_assert(set_result == 1)
+
+    def get_issuer(self):
+        """
+        Return the issuer of this certificate.
+
+        This creates a new :class:`X509Name` that wraps the underlying issuer
+        name field on the certificate. Modifying it will modify the underlying
+        certificate, and will have the effect of modifying any other
+        :class:`X509Name` that refers to this issuer.
+
+        :return: The issuer of this certificate.
+        :rtype: :class:`X509Name`
+        """
+        name = self._get_name(_lib.X509_get_issuer_name)
+        self._issuer_invalidator.add(name)
+        return name
+
+    def set_issuer(self, issuer):
+        """
+        Set the issuer of this certificate.
+
+        :param issuer: The issuer.
+        :type issuer: :py:class:`X509Name`
+
+        :return: ``None``
+        """
+        self._set_name(_lib.X509_set_issuer_name, issuer)
+        self._issuer_invalidator.clear()
+
+    def get_subject(self):
+        """
+        Return the subject of this certificate.
+
+        This creates a new :class:`X509Name` that wraps the underlying subject
+        name field on the certificate. Modifying it will modify the underlying
+        certificate, and will have the effect of modifying any other
+        :class:`X509Name` that refers to this subject.
+
+        :return: The subject of this certificate.
+        :rtype: :class:`X509Name`
+        """
+        name = self._get_name(_lib.X509_get_subject_name)
+        self._subject_invalidator.add(name)
+        return name
+
+    def set_subject(self, subject):
+        """
+        Set the subject of this certificate.
+
+        :param subject: The subject.
+        :type subject: :py:class:`X509Name`
+
+        :return: ``None``
+        """
+        self._set_name(_lib.X509_set_subject_name, subject)
+        self._subject_invalidator.clear()
+
+    def get_extension_count(self):
+        """
+        Get the number of extensions on this certificate.
+
+        :return: The number of extensions.
+        :rtype: :py:class:`int`
+
+        .. versionadded:: 0.12
+        """
+        return _lib.X509_get_ext_count(self._x509)
+
+    def add_extensions(self, extensions):
+        """
+        Add extensions to the certificate.
+
+        :param extensions: The extensions to add.
+        :type extensions: An iterable of :py:class:`X509Extension` objects.
+        :return: ``None``
+        """
+        for ext in extensions:
+            if not isinstance(ext, X509Extension):
+                raise ValueError("One of the elements is not an X509Extension")
+
+            add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
+            if not add_result:
+                _raise_current_error()
+
+    def get_extension(self, index):
+        """
+        Get a specific extension of the certificate by index.
+
+        Extensions on a certificate are kept in order. The index
+        parameter selects which extension will be returned.
+
+        :param int index: The index of the extension to retrieve.
+        :return: The extension at the specified index.
+        :rtype: :py:class:`X509Extension`
+        :raises IndexError: If the extension index was out of bounds.
+
+        .. versionadded:: 0.12
+        """
+        ext = X509Extension.__new__(X509Extension)
+        ext._extension = _lib.X509_get_ext(self._x509, index)
+        if ext._extension == _ffi.NULL:
+            raise IndexError("extension index out of bounds")
+
+        extension = _lib.X509_EXTENSION_dup(ext._extension)
+        ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
+        return ext
+
+
+class X509StoreFlags(object):
+    """
+    Flags for X509 verification, used to change the behavior of
+    :class:`X509Store`.
+
+    See `OpenSSL Verification Flags`_ for details.
+
+    .. _OpenSSL Verification Flags:
+        https://www.openssl.org/docs/manmaster/man3/X509_VERIFY_PARAM_set_flags.html
+    """
+
+    CRL_CHECK = _lib.X509_V_FLAG_CRL_CHECK
+    CRL_CHECK_ALL = _lib.X509_V_FLAG_CRL_CHECK_ALL
+    IGNORE_CRITICAL = _lib.X509_V_FLAG_IGNORE_CRITICAL
+    X509_STRICT = _lib.X509_V_FLAG_X509_STRICT
+    ALLOW_PROXY_CERTS = _lib.X509_V_FLAG_ALLOW_PROXY_CERTS
+    POLICY_CHECK = _lib.X509_V_FLAG_POLICY_CHECK
+    EXPLICIT_POLICY = _lib.X509_V_FLAG_EXPLICIT_POLICY
+    INHIBIT_MAP = _lib.X509_V_FLAG_INHIBIT_MAP
+    NOTIFY_POLICY = _lib.X509_V_FLAG_NOTIFY_POLICY
+    CHECK_SS_SIGNATURE = _lib.X509_V_FLAG_CHECK_SS_SIGNATURE
+
+
+class X509Store(object):
+    """
+    An X.509 store.
+
+    An X.509 store is used to describe a context in which to verify a
+    certificate. A description of a context may include a set of certificates
+    to trust, a set of certificate revocation lists, verification flags and
+    more.
+
+    An X.509 store, being only a description, cannot be used by itself to
+    verify a certificate. To carry out the actual verification process, see
+    :class:`X509StoreContext`.
+    """
+
+    def __init__(self):
+        store = _lib.X509_STORE_new()
+        self._store = _ffi.gc(store, _lib.X509_STORE_free)
+
+    def add_cert(self, cert):
+        """
+        Adds a trusted certificate to this store.
+
+        Adding a certificate with this method adds this certificate as a
+        *trusted* certificate.
+
+        :param X509 cert: The certificate to add to this store.
+
+        :raises TypeError: If the certificate is not an :class:`X509`.
+
+        :raises OpenSSL.crypto.Error: If OpenSSL was unhappy with your
+            certificate.
+
+        :return: ``None`` if the certificate was added successfully.
+        """
+        if not isinstance(cert, X509):
+            raise TypeError()
+
+        res = _lib.X509_STORE_add_cert(self._store, cert._x509)
+        _openssl_assert(res == 1)
+
+    def add_crl(self, crl):
+        """
+        Add a certificate revocation list to this store.
+
+        The certificate revocation lists added to a store will only be used if
+        the associated flags are configured to check certificate revocation
+        lists.
+
+        .. versionadded:: 16.1.0
+
+        :param CRL crl: The certificate revocation list to add to this store.
+        :return: ``None`` if the certificate revocation list was added
+            successfully.
+        """
+        _openssl_assert(_lib.X509_STORE_add_crl(self._store, crl._crl) != 0)
+
+    def set_flags(self, flags):
+        """
+        Set verification flags to this store.
+
+        Verification flags can be combined by oring them together.
+
+        .. note::
+
+          Setting a verification flag sometimes requires clients to add
+          additional information to the store, otherwise a suitable error will
+          be raised.
+
+          For example, in setting flags to enable CRL checking a
+          suitable CRL must be added to the store otherwise an error will be
+          raised.
+
+        .. versionadded:: 16.1.0
+
+        :param int flags: The verification flags to set on this store.
+            See :class:`X509StoreFlags` for available constants.
+        :return: ``None`` if the verification flags were successfully set.
+        """
+        _openssl_assert(_lib.X509_STORE_set_flags(self._store, flags) != 0)
+
+    def set_time(self, vfy_time):
+        """
+        Set the time against which the certificates are verified.
+
+        Normally the current time is used.
+
+        .. note::
+
+          For example, you can determine if a certificate was valid at a given
+          time.
+
+        .. versionadded:: 17.0.0
+
+        :param datetime vfy_time: The verification time to set on this store.
+        :return: ``None`` if the verification time was successfully set.
+        """
+        param = _lib.X509_VERIFY_PARAM_new()
+        param = _ffi.gc(param, _lib.X509_VERIFY_PARAM_free)
+
+        _lib.X509_VERIFY_PARAM_set_time(
+            param, calendar.timegm(vfy_time.timetuple())
+        )
+        _openssl_assert(_lib.X509_STORE_set1_param(self._store, param) != 0)
+
+    def load_locations(self, cafile, capath=None):
+        """
+        Let X509Store know where we can find trusted certificates for the
+        certificate chain.  Note that the certificates have to be in PEM
+        format.
+
+        If *capath* is passed, it must be a directory prepared using the
+        ``c_rehash`` tool included with OpenSSL.  Either, but not both, of
+        *cafile* or *capath* may be ``None``.
+
+        .. note::
+
+          Both *cafile* and *capath* may be set simultaneously.
+
+          Call this method multiple times to add more than one location.
+          For example, CA certificates, and certificate revocation list bundles
+          may be passed in *cafile* in subsequent calls to this method.
+
+        .. versionadded:: 20.0
+
+        :param cafile: In which file we can find the certificates (``bytes`` or
+                       ``unicode``).
+        :param capath: In which directory we can find the certificates
+                       (``bytes`` or ``unicode``).
+
+        :return: ``None`` if the locations were set successfully.
+
+        :raises OpenSSL.crypto.Error: If both *cafile* and *capath* is ``None``
+            or the locations could not be set for any reason.
+
+        """
+        if cafile is None:
+            cafile = _ffi.NULL
+        else:
+            cafile = _path_bytes(cafile)
+
+        if capath is None:
+            capath = _ffi.NULL
+        else:
+            capath = _path_bytes(capath)
+
+        load_result = _lib.X509_STORE_load_locations(
+            self._store, cafile, capath
+        )
+        if not load_result:
+            _raise_current_error()
+
+
+class X509StoreContextError(Exception):
+    """
+    An exception raised when an error occurred while verifying a certificate
+    using `OpenSSL.X509StoreContext.verify_certificate`.
+
+    :ivar certificate: The certificate which caused verificate failure.
+    :type certificate: :class:`X509`
+    """
+
+    def __init__(self, message, certificate):
+        super(X509StoreContextError, self).__init__(message)
+        self.certificate = certificate
+
+
+class X509StoreContext(object):
+    """
+    An X.509 store context.
+
+    An X.509 store context is used to carry out the actual verification process
+    of a certificate in a described context. For describing such a context, see
+    :class:`X509Store`.
+
+    :ivar _store_ctx: The underlying X509_STORE_CTX structure used by this
+        instance.  It is dynamically allocated and automatically garbage
+        collected.
+    :ivar _store: See the ``store`` ``__init__`` parameter.
+    :ivar _cert: See the ``certificate`` ``__init__`` parameter.
+    :ivar _chain: See the ``chain`` ``__init__`` parameter.
+    :param X509Store store: The certificates which will be trusted for the
+        purposes of any verifications.
+    :param X509 certificate: The certificate to be verified.
+    :param chain: List of untrusted certificates that may be used for building
+        the certificate chain. May be ``None``.
+    :type chain: :class:`list` of :class:`X509`
+    """
+
+    def __init__(self, store, certificate, chain=None):
+        store_ctx = _lib.X509_STORE_CTX_new()
+        self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
+        self._store = store
+        self._cert = certificate
+        self._chain = self._build_certificate_stack(chain)
+        # Make the store context available for use after instantiating this
+        # class by initializing it now. Per testing, subsequent calls to
+        # :meth:`_init` have no adverse affect.
+        self._init()
+
+    @staticmethod
+    def _build_certificate_stack(certificates):
+        def cleanup(s):
+            # Equivalent to sk_X509_pop_free, but we don't
+            # currently have a CFFI binding for that available
+            for i in range(_lib.sk_X509_num(s)):
+                x = _lib.sk_X509_value(s, i)
+                _lib.X509_free(x)
+            _lib.sk_X509_free(s)
+
+        if certificates is None or len(certificates) == 0:
+            return _ffi.NULL
+
+        stack = _lib.sk_X509_new_null()
+        _openssl_assert(stack != _ffi.NULL)
+        stack = _ffi.gc(stack, cleanup)
+
+        for cert in certificates:
+            if not isinstance(cert, X509):
+                raise TypeError("One of the elements is not an X509 instance")
+
+            _openssl_assert(_lib.X509_up_ref(cert._x509) > 0)
+            if _lib.sk_X509_push(stack, cert._x509) <= 0:
+                _lib.X509_free(cert._x509)
+                _raise_current_error()
+
+        return stack
+
+    def _init(self):
+        """
+        Set up the store context for a subsequent verification operation.
+
+        Calling this method more than once without first calling
+        :meth:`_cleanup` will leak memory.
+        """
+        ret = _lib.X509_STORE_CTX_init(
+            self._store_ctx, self._store._store, self._cert._x509, self._chain
+        )
+        if ret <= 0:
+            _raise_current_error()
+
+    def _cleanup(self):
+        """
+        Internally cleans up the store context.
+
+        The store context can then be reused with a new call to :meth:`_init`.
+        """
+        _lib.X509_STORE_CTX_cleanup(self._store_ctx)
+
+    def _exception_from_context(self):
+        """
+        Convert an OpenSSL native context error failure into a Python
+        exception.
+
+        When a call to native OpenSSL X509_verify_cert fails, additional
+        information about the failure can be obtained from the store context.
+        """
+        errors = [
+            _lib.X509_STORE_CTX_get_error(self._store_ctx),
+            _lib.X509_STORE_CTX_get_error_depth(self._store_ctx),
+            _ffi.string(
+                _lib.X509_verify_cert_error_string(
+                    _lib.X509_STORE_CTX_get_error(self._store_ctx)
+                )
+            ).decode("utf-8"),
+        ]
+        # A context error should always be associated with a certificate, so we
+        # expect this call to never return :class:`None`.
+        _x509 = _lib.X509_STORE_CTX_get_current_cert(self._store_ctx)
+        _cert = _lib.X509_dup(_x509)
+        pycert = X509._from_raw_x509_ptr(_cert)
+        return X509StoreContextError(errors, pycert)
+
+    def set_store(self, store):
+        """
+        Set the context's X.509 store.
+
+        .. versionadded:: 0.15
+
+        :param X509Store store: The store description which will be used for
+            the purposes of any *future* verifications.
+        """
+        self._store = store
+
+    def verify_certificate(self):
+        """
+        Verify a certificate in a context.
+
+        .. versionadded:: 0.15
+
+        :raises X509StoreContextError: If an error occurred when validating a
+          certificate in the context. Sets ``certificate`` attribute to
+          indicate which certificate caused the error.
+        """
+        # Always re-initialize the store context in case
+        # :meth:`verify_certificate` is called multiple times.
+        #
+        # :meth:`_init` is called in :meth:`__init__` so _cleanup is called
+        # before _init to ensure memory is not leaked.
+        self._cleanup()
+        self._init()
+        ret = _lib.X509_verify_cert(self._store_ctx)
+        self._cleanup()
+        if ret <= 0:
+            raise self._exception_from_context()
+
+    def get_verified_chain(self):
+        """
+        Verify a certificate in a context and return the complete validated
+        chain.
+
+        :raises X509StoreContextError: If an error occurred when validating a
+          certificate in the context. Sets ``certificate`` attribute to
+          indicate which certificate caused the error.
+
+        .. versionadded:: 20.0
+        """
+        # Always re-initialize the store context in case
+        # :meth:`verify_certificate` is called multiple times.
+        #
+        # :meth:`_init` is called in :meth:`__init__` so _cleanup is called
+        # before _init to ensure memory is not leaked.
+        self._cleanup()
+        self._init()
+        ret = _lib.X509_verify_cert(self._store_ctx)
+        if ret <= 0:
+            self._cleanup()
+            raise self._exception_from_context()
+
+        # Note: X509_STORE_CTX_get1_chain returns a deep copy of the chain.
+        cert_stack = _lib.X509_STORE_CTX_get1_chain(self._store_ctx)
+        _openssl_assert(cert_stack != _ffi.NULL)
+
+        result = []
+        for i in range(_lib.sk_X509_num(cert_stack)):
+            cert = _lib.sk_X509_value(cert_stack, i)
+            _openssl_assert(cert != _ffi.NULL)
+            pycert = X509._from_raw_x509_ptr(cert)
+            result.append(pycert)
+
+        # Free the stack but not the members which are freed by the X509 class.
+        _lib.sk_X509_free(cert_stack)
+        self._cleanup()
+        return result
+
+
+def load_certificate(type, buffer):
+    """
+    Load a certificate (X509) from the string *buffer* encoded with the
+    type *type*.
+
+    :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
+
+    :param bytes buffer: The buffer the certificate is stored in
+
+    :return: The X509 object
+    """
+    if isinstance(buffer, str):
+        buffer = buffer.encode("ascii")
+
+    bio = _new_mem_buf(buffer)
+
+    if type == FILETYPE_PEM:
+        x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
+    elif type == FILETYPE_ASN1:
+        x509 = _lib.d2i_X509_bio(bio, _ffi.NULL)
+    else:
+        raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
+
+    if x509 == _ffi.NULL:
+        _raise_current_error()
+
+    return X509._from_raw_x509_ptr(x509)
+
+
+def dump_certificate(type, cert):
+    """
+    Dump the certificate *cert* into a buffer string encoded with the type
+    *type*.
+
+    :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
+        FILETYPE_TEXT)
+    :param cert: The certificate to dump
+    :return: The buffer with the dumped certificate in
+    """
+    bio = _new_mem_buf()
+
+    if type == FILETYPE_PEM:
+        result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
+    elif type == FILETYPE_ASN1:
+        result_code = _lib.i2d_X509_bio(bio, cert._x509)
+    elif type == FILETYPE_TEXT:
+        result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
+    else:
+        raise ValueError(
+            "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
+            "FILETYPE_TEXT"
+        )
+
+    _openssl_assert(result_code == 1)
+    return _bio_to_string(bio)
+
+
+def dump_publickey(type, pkey):
+    """
+    Dump a public key to a buffer.
+
+    :param type: The file type (one of :data:`FILETYPE_PEM` or
+        :data:`FILETYPE_ASN1`).
+    :param PKey pkey: The public key to dump
+    :return: The buffer with the dumped key in it.
+    :rtype: bytes
+    """
+    bio = _new_mem_buf()
+    if type == FILETYPE_PEM:
+        write_bio = _lib.PEM_write_bio_PUBKEY
+    elif type == FILETYPE_ASN1:
+        write_bio = _lib.i2d_PUBKEY_bio
+    else:
+        raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
+
+    result_code = write_bio(bio, pkey._pkey)
+    if result_code != 1:  # pragma: no cover
+        _raise_current_error()
+
+    return _bio_to_string(bio)
+
+
+def dump_privatekey(type, pkey, cipher=None, passphrase=None):
+    """
+    Dump the private key *pkey* into a buffer string encoded with the type
+    *type*.  Optionally (if *type* is :const:`FILETYPE_PEM`) encrypting it
+    using *cipher* and *passphrase*.
+
+    :param type: The file type (one of :const:`FILETYPE_PEM`,
+        :const:`FILETYPE_ASN1`, or :const:`FILETYPE_TEXT`)
+    :param PKey pkey: The PKey to dump
+    :param cipher: (optional) if encrypted PEM format, the cipher to use
+    :param passphrase: (optional) if encrypted PEM format, this can be either
+        the passphrase to use, or a callback for providing the passphrase.
+
+    :return: The buffer with the dumped key in
+    :rtype: bytes
+    """
+    bio = _new_mem_buf()
+
+    if not isinstance(pkey, PKey):
+        raise TypeError("pkey must be a PKey")
+
+    if cipher is not None:
+        if passphrase is None:
+            raise TypeError(
+                "if a value is given for cipher "
+                "one must also be given for passphrase"
+            )
+        cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
+        if cipher_obj == _ffi.NULL:
+            raise ValueError("Invalid cipher name")
+    else:
+        cipher_obj = _ffi.NULL
+
+    helper = _PassphraseHelper(type, passphrase)
+    if type == FILETYPE_PEM:
+        result_code = _lib.PEM_write_bio_PrivateKey(
+            bio,
+            pkey._pkey,
+            cipher_obj,
+            _ffi.NULL,
+            0,
+            helper.callback,
+            helper.callback_args,
+        )
+        helper.raise_if_problem()
+    elif type == FILETYPE_ASN1:
+        result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
+    elif type == FILETYPE_TEXT:
+        if _lib.EVP_PKEY_id(pkey._pkey) != _lib.EVP_PKEY_RSA:
+            raise TypeError("Only RSA keys are supported for FILETYPE_TEXT")
+
+        rsa = _ffi.gc(_lib.EVP_PKEY_get1_RSA(pkey._pkey), _lib.RSA_free)
+        result_code = _lib.RSA_print(bio, rsa, 0)
+    else:
+        raise ValueError(
+            "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
+            "FILETYPE_TEXT"
+        )
+
+    _openssl_assert(result_code != 0)
+
+    return _bio_to_string(bio)
+
+
+class Revoked(object):
+    """
+    A certificate revocation.
+    """
+
+    # https://www.openssl.org/docs/manmaster/man5/x509v3_config.html#CRL-distribution-points
+    # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
+    # OCSP_crl_reason_str.  We use the latter, just like the command line
+    # program.
+    _crl_reasons = [
+        b"unspecified",
+        b"keyCompromise",
+        b"CACompromise",
+        b"affiliationChanged",
+        b"superseded",
+        b"cessationOfOperation",
+        b"certificateHold",
+        # b"removeFromCRL",
+    ]
+
+    def __init__(self):
+        revoked = _lib.X509_REVOKED_new()
+        self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
+
+    def set_serial(self, hex_str):
+        """
+        Set the serial number.
+
+        The serial number is formatted as a hexadecimal number encoded in
+        ASCII.
+
+        :param bytes hex_str: The new serial number.
+
+        :return: ``None``
+        """
+        bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
+        bignum_ptr = _ffi.new("BIGNUM**")
+        bignum_ptr[0] = bignum_serial
+        bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
+        if not bn_result:
+            raise ValueError("bad hex string")
+
+        asn1_serial = _ffi.gc(
+            _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
+            _lib.ASN1_INTEGER_free,
+        )
+        _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
+
+    def get_serial(self):
+        """
+        Get the serial number.
+
+        The serial number is formatted as a hexadecimal number encoded in
+        ASCII.
+
+        :return: The serial number.
+        :rtype: bytes
+        """
+        bio = _new_mem_buf()
+
+        asn1_int = _lib.X509_REVOKED_get0_serialNumber(self._revoked)
+        _openssl_assert(asn1_int != _ffi.NULL)
+        result = _lib.i2a_ASN1_INTEGER(bio, asn1_int)
+        _openssl_assert(result >= 0)
+        return _bio_to_string(bio)
+
+    def _delete_reason(self):
+        for i in range(_lib.X509_REVOKED_get_ext_count(self._revoked)):
+            ext = _lib.X509_REVOKED_get_ext(self._revoked, i)
+            obj = _lib.X509_EXTENSION_get_object(ext)
+            if _lib.OBJ_obj2nid(obj) == _lib.NID_crl_reason:
+                _lib.X509_EXTENSION_free(ext)
+                _lib.X509_REVOKED_delete_ext(self._revoked, i)
+                break
+
+    def set_reason(self, reason):
+        """
+        Set the reason of this revocation.
+
+        If :data:`reason` is ``None``, delete the reason instead.
+
+        :param reason: The reason string.
+        :type reason: :class:`bytes` or :class:`NoneType`
+
+        :return: ``None``
+
+        .. seealso::
+
+            :meth:`all_reasons`, which gives you a list of all supported
+            reasons which you might pass to this method.
+        """
+        if reason is None:
+            self._delete_reason()
+        elif not isinstance(reason, bytes):
+            raise TypeError("reason must be None or a byte string")
+        else:
+            reason = reason.lower().replace(b" ", b"")
+            reason_code = [r.lower() for r in self._crl_reasons].index(reason)
+
+            new_reason_ext = _lib.ASN1_ENUMERATED_new()
+            _openssl_assert(new_reason_ext != _ffi.NULL)
+            new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
+
+            set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
+            _openssl_assert(set_result != _ffi.NULL)
+
+            self._delete_reason()
+            add_result = _lib.X509_REVOKED_add1_ext_i2d(
+                self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0
+            )
+            _openssl_assert(add_result == 1)
+
+    def get_reason(self):
+        """
+        Get the reason of this revocation.
+
+        :return: The reason, or ``None`` if there is none.
+        :rtype: bytes or NoneType
+
+        .. seealso::
+
+            :meth:`all_reasons`, which gives you a list of all supported
+            reasons this method might return.
+        """
+        for i in range(_lib.X509_REVOKED_get_ext_count(self._revoked)):
+            ext = _lib.X509_REVOKED_get_ext(self._revoked, i)
+            obj = _lib.X509_EXTENSION_get_object(ext)
+            if _lib.OBJ_obj2nid(obj) == _lib.NID_crl_reason:
+                bio = _new_mem_buf()
+
+                print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
+                if not print_result:
+                    print_result = _lib.M_ASN1_OCTET_STRING_print(
+                        bio, _lib.X509_EXTENSION_get_data(ext)
+                    )
+                    _openssl_assert(print_result != 0)
+
+                return _bio_to_string(bio)
+
+    def all_reasons(self):
+        """
+        Return a list of all the supported reason strings.
+
+        This list is a copy; modifying it does not change the supported reason
+        strings.
+
+        :return: A list of reason strings.
+        :rtype: :class:`list` of :class:`bytes`
+        """
+        return self._crl_reasons[:]
+
+    def set_rev_date(self, when):
+        """
+        Set the revocation timestamp.
+
+        :param bytes when: The timestamp of the revocation,
+            as ASN.1 TIME.
+        :return: ``None``
+        """
+        dt = _lib.X509_REVOKED_get0_revocationDate(self._revoked)
+        return _set_asn1_time(dt, when)
+
+    def get_rev_date(self):
+        """
+        Get the revocation timestamp.
+
+        :return: The timestamp of the revocation, as ASN.1 TIME.
+        :rtype: bytes
+        """
+        dt = _lib.X509_REVOKED_get0_revocationDate(self._revoked)
+        return _get_asn1_time(dt)
+
+
+class CRL(object):
+    """
+    A certificate revocation list.
+    """
+
+    def __init__(self):
+        crl = _lib.X509_CRL_new()
+        self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
+
+    def to_cryptography(self):
+        """
+        Export as a ``cryptography`` CRL.
+
+        :rtype: ``cryptography.x509.CertificateRevocationList``
+
+        .. versionadded:: 17.1.0
+        """
+        from cryptography.x509 import load_der_x509_crl
+
+        der = dump_crl(FILETYPE_ASN1, self)
+
+        backend = _get_backend()
+        return load_der_x509_crl(der, backend)
+
+    @classmethod
+    def from_cryptography(cls, crypto_crl):
+        """
+        Construct based on a ``cryptography`` *crypto_crl*.
+
+        :param crypto_crl: A ``cryptography`` certificate revocation list
+        :type crypto_crl: ``cryptography.x509.CertificateRevocationList``
+
+        :rtype: CRL
+
+        .. versionadded:: 17.1.0
+        """
+        if not isinstance(crypto_crl, x509.CertificateRevocationList):
+            raise TypeError("Must be a certificate revocation list")
+
+        from cryptography.hazmat.primitives.serialization import Encoding
+
+        der = crypto_crl.public_bytes(Encoding.DER)
+        return load_crl(FILETYPE_ASN1, der)
+
+    def get_revoked(self):
+        """
+        Return the revocations in this certificate revocation list.
+
+        These revocations will be provided by value, not by reference.
+        That means it's okay to mutate them: it won't affect this CRL.
+
+        :return: The revocations in this CRL.
+        :rtype: :class:`tuple` of :class:`Revocation`
+        """
+        results = []
+        revoked_stack = _lib.X509_CRL_get_REVOKED(self._crl)
+        for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
+            revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
+            revoked_copy = _lib.X509_REVOKED_dup(revoked)
+            pyrev = Revoked.__new__(Revoked)
+            pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
+            results.append(pyrev)
+        if results:
+            return tuple(results)
+
+    def add_revoked(self, revoked):
+        """
+        Add a revoked (by value not reference) to the CRL structure
+
+        This revocation will be added by value, not by reference. That
+        means it's okay to mutate it after adding: it won't affect
+        this CRL.
+
+        :param Revoked revoked: The new revocation.
+        :return: ``None``
+        """
+        copy = _lib.X509_REVOKED_dup(revoked._revoked)
+        _openssl_assert(copy != _ffi.NULL)
+
+        add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
+        _openssl_assert(add_result != 0)
+
+    def get_issuer(self):
+        """
+        Get the CRL's issuer.
+
+        .. versionadded:: 16.1.0
+
+        :rtype: X509Name
+        """
+        _issuer = _lib.X509_NAME_dup(_lib.X509_CRL_get_issuer(self._crl))
+        _openssl_assert(_issuer != _ffi.NULL)
+        _issuer = _ffi.gc(_issuer, _lib.X509_NAME_free)
+        issuer = X509Name.__new__(X509Name)
+        issuer._name = _issuer
+        return issuer
+
+    def set_version(self, version):
+        """
+        Set the CRL version.
+
+        .. versionadded:: 16.1.0
+
+        :param int version: The version of the CRL.
+        :return: ``None``
+        """
+        _openssl_assert(_lib.X509_CRL_set_version(self._crl, version) != 0)
+
+    def _set_boundary_time(self, which, when):
+        return _set_asn1_time(which(self._crl), when)
+
+    def set_lastUpdate(self, when):
+        """
+        Set when the CRL was last updated.
+
+        The timestamp is formatted as an ASN.1 TIME::
+
+            YYYYMMDDhhmmssZ
+
+        .. versionadded:: 16.1.0
+
+        :param bytes when: A timestamp string.
+        :return: ``None``
+        """
+        return self._set_boundary_time(_lib.X509_CRL_get0_lastUpdate, when)
+
+    def set_nextUpdate(self, when):
+        """
+        Set when the CRL will next be updated.
+
+        The timestamp is formatted as an ASN.1 TIME::
+
+            YYYYMMDDhhmmssZ
+
+        .. versionadded:: 16.1.0
+
+        :param bytes when: A timestamp string.
+        :return: ``None``
+        """
+        return self._set_boundary_time(_lib.X509_CRL_get0_nextUpdate, when)
+
+    def sign(self, issuer_cert, issuer_key, digest):
+        """
+        Sign the CRL.
+
+        Signing a CRL enables clients to associate the CRL itself with an
+        issuer. Before a CRL is meaningful to other OpenSSL functions, it must
+        be signed by an issuer.
+
+        This method implicitly sets the issuer's name based on the issuer
+        certificate and private key used to sign the CRL.
+
+        .. versionadded:: 16.1.0
+
+        :param X509 issuer_cert: The issuer's certificate.
+        :param PKey issuer_key: The issuer's private key.
+        :param bytes digest: The digest method to sign the CRL with.
+        """
+        digest_obj = _lib.EVP_get_digestbyname(digest)
+        _openssl_assert(digest_obj != _ffi.NULL)
+        _lib.X509_CRL_set_issuer_name(
+            self._crl, _lib.X509_get_subject_name(issuer_cert._x509)
+        )
+        _lib.X509_CRL_sort(self._crl)
+        result = _lib.X509_CRL_sign(self._crl, issuer_key._pkey, digest_obj)
+        _openssl_assert(result != 0)
+
+    def export(
+        self, cert, key, type=FILETYPE_PEM, days=100, digest=_UNSPECIFIED
+    ):
+        """
+        Export the CRL as a string.
+
+        :param X509 cert: The certificate used to sign the CRL.
+        :param PKey key: The key used to sign the CRL.
+        :param int type: The export format, either :data:`FILETYPE_PEM`,
+            :data:`FILETYPE_ASN1`, or :data:`FILETYPE_TEXT`.
+        :param int days: The number of days until the next update of this CRL.
+        :param bytes digest: The name of the message digest to use (eg
+            ``b"sha256"``).
+        :rtype: bytes
+        """
+
+        if not isinstance(cert, X509):
+            raise TypeError("cert must be an X509 instance")
+        if not isinstance(key, PKey):
+            raise TypeError("key must be a PKey instance")
+        if not isinstance(type, int):
+            raise TypeError("type must be an integer")
+
+        if digest is _UNSPECIFIED:
+            raise TypeError("digest must be provided")
+
+        digest_obj = _lib.EVP_get_digestbyname(digest)
+        if digest_obj == _ffi.NULL:
+            raise ValueError("No such digest method")
+
+        bio = _lib.BIO_new(_lib.BIO_s_mem())
+        _openssl_assert(bio != _ffi.NULL)
+
+        # A scratch time object to give different values to different CRL
+        # fields
+        sometime = _lib.ASN1_TIME_new()
+        _openssl_assert(sometime != _ffi.NULL)
+
+        _lib.X509_gmtime_adj(sometime, 0)
+        _lib.X509_CRL_set1_lastUpdate(self._crl, sometime)
+
+        _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
+        _lib.X509_CRL_set1_nextUpdate(self._crl, sometime)
+
+        _lib.X509_CRL_set_issuer_name(
+            self._crl, _lib.X509_get_subject_name(cert._x509)
+        )
+
+        sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, digest_obj)
+        if not sign_result:
+            _raise_current_error()
+
+        return dump_crl(type, self)
+
+
+class PKCS7(object):
+    def type_is_signed(self):
+        """
+        Check if this NID_pkcs7_signed object
+
+        :return: True if the PKCS7 is of type signed
+        """
+        return bool(_lib.PKCS7_type_is_signed(self._pkcs7))
+
+    def type_is_enveloped(self):
+        """
+        Check if this NID_pkcs7_enveloped object
+
+        :returns: True if the PKCS7 is of type enveloped
+        """
+        return bool(_lib.PKCS7_type_is_enveloped(self._pkcs7))
+
+    def type_is_signedAndEnveloped(self):
+        """
+        Check if this NID_pkcs7_signedAndEnveloped object
+
+        :returns: True if the PKCS7 is of type signedAndEnveloped
+        """
+        return bool(_lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7))
+
+    def type_is_data(self):
+        """
+        Check if this NID_pkcs7_data object
+
+        :return: True if the PKCS7 is of type data
+        """
+        return bool(_lib.PKCS7_type_is_data(self._pkcs7))
+
+    def get_type_name(self):
+        """
+        Returns the type name of the PKCS7 structure
+
+        :return: A string with the typename
+        """
+        nid = _lib.OBJ_obj2nid(self._pkcs7.type)
+        string_type = _lib.OBJ_nid2sn(nid)
+        return _ffi.string(string_type)
+
+
+class PKCS12(object):
+    """
+    A PKCS #12 archive.
+    """
+
+    def __init__(self):
+        self._pkey = None
+        self._cert = None
+        self._cacerts = None
+        self._friendlyname = None
+
+    def get_certificate(self):
+        """
+        Get the certificate in the PKCS #12 structure.
+
+        :return: The certificate, or :py:const:`None` if there is none.
+        :rtype: :py:class:`X509` or :py:const:`None`
+        """
+        return self._cert
+
+    def set_certificate(self, cert):
+        """
+        Set the certificate in the PKCS #12 structure.
+
+        :param cert: The new certificate, or :py:const:`None` to unset it.
+        :type cert: :py:class:`X509` or :py:const:`None`
+
+        :return: ``None``
+        """
+        if not isinstance(cert, X509):
+            raise TypeError("cert must be an X509 instance")
+        self._cert = cert
+
+    def get_privatekey(self):
+        """
+        Get the private key in the PKCS #12 structure.
+
+        :return: The private key, or :py:const:`None` if there is none.
+        :rtype: :py:class:`PKey`
+        """
+        return self._pkey
+
+    def set_privatekey(self, pkey):
+        """
+        Set the certificate portion of the PKCS #12 structure.
+
+        :param pkey: The new private key, or :py:const:`None` to unset it.
+        :type pkey: :py:class:`PKey` or :py:const:`None`
+
+        :return: ``None``
+        """
+        if not isinstance(pkey, PKey):
+            raise TypeError("pkey must be a PKey instance")
+        self._pkey = pkey
+
+    def get_ca_certificates(self):
+        """
+        Get the CA certificates in the PKCS #12 structure.
+
+        :return: A tuple with the CA certificates in the chain, or
+            :py:const:`None` if there are none.
+        :rtype: :py:class:`tuple` of :py:class:`X509` or :py:const:`None`
+        """
+        if self._cacerts is not None:
+            return tuple(self._cacerts)
+
+    def set_ca_certificates(self, cacerts):
+        """
+        Replace or set the CA certificates within the PKCS12 object.
+
+        :param cacerts: The new CA certificates, or :py:const:`None` to unset
+            them.
+        :type cacerts: An iterable of :py:class:`X509` or :py:const:`None`
+
+        :return: ``None``
+        """
+        if cacerts is None:
+            self._cacerts = None
+        else:
+            cacerts = list(cacerts)
+            for cert in cacerts:
+                if not isinstance(cert, X509):
+                    raise TypeError(
+                        "iterable must only contain X509 instances"
+                    )
+            self._cacerts = cacerts
+
+    def set_friendlyname(self, name):
+        """
+        Set the friendly name in the PKCS #12 structure.
+
+        :param name: The new friendly name, or :py:const:`None` to unset.
+        :type name: :py:class:`bytes` or :py:const:`None`
+
+        :return: ``None``
+        """
+        if name is None:
+            self._friendlyname = None
+        elif not isinstance(name, bytes):
+            raise TypeError(
+                "name must be a byte string or None (not %r)" % (name,)
+            )
+        self._friendlyname = name
+
+    def get_friendlyname(self):
+        """
+        Get the friendly name in the PKCS# 12 structure.
+
+        :returns: The friendly name,  or :py:const:`None` if there is none.
+        :rtype: :py:class:`bytes` or :py:const:`None`
+        """
+        return self._friendlyname
+
+    def export(self, passphrase=None, iter=2048, maciter=1):
+        """
+        Dump a PKCS12 object as a string.
+
+        For more information, see the :c:func:`PKCS12_create` man page.
+
+        :param passphrase: The passphrase used to encrypt the structure. Unlike
+            some other passphrase arguments, this *must* be a string, not a
+            callback.
+        :type passphrase: :py:data:`bytes`
+
+        :param iter: Number of times to repeat the encryption step.
+        :type iter: :py:data:`int`
+
+        :param maciter: Number of times to repeat the MAC step.
+        :type maciter: :py:data:`int`
+
+        :return: The string representation of the PKCS #12 structure.
+        :rtype:
+        """
+        passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
+
+        if self._cacerts is None:
+            cacerts = _ffi.NULL
+        else:
+            cacerts = _lib.sk_X509_new_null()
+            cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
+            for cert in self._cacerts:
+                _lib.sk_X509_push(cacerts, cert._x509)
+
+        if passphrase is None:
+            passphrase = _ffi.NULL
+
+        friendlyname = self._friendlyname
+        if friendlyname is None:
+            friendlyname = _ffi.NULL
+
+        if self._pkey is None:
+            pkey = _ffi.NULL
+        else:
+            pkey = self._pkey._pkey
+
+        if self._cert is None:
+            cert = _ffi.NULL
+        else:
+            cert = self._cert._x509
+
+        pkcs12 = _lib.PKCS12_create(
+            passphrase,
+            friendlyname,
+            pkey,
+            cert,
+            cacerts,
+            _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
+            _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
+            iter,
+            maciter,
+            0,
+        )
+        if pkcs12 == _ffi.NULL:
+            _raise_current_error()
+        pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
+
+        bio = _new_mem_buf()
+        _lib.i2d_PKCS12_bio(bio, pkcs12)
+        return _bio_to_string(bio)
+
+
+class NetscapeSPKI(object):
+    """
+    A Netscape SPKI object.
+    """
+
+    def __init__(self):
+        spki = _lib.NETSCAPE_SPKI_new()
+        self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
+
+    def sign(self, pkey, digest):
+        """
+        Sign the certificate request with this key and digest type.
+
+        :param pkey: The private key to sign with.
+        :type pkey: :py:class:`PKey`
+
+        :param digest: The message digest to use.
+        :type digest: :py:class:`str`
+
+        :return: ``None``
+        """
+        if pkey._only_public:
+            raise ValueError("Key has only public part")
+
+        if not pkey._initialized:
+            raise ValueError("Key is uninitialized")
+
+        digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
+        if digest_obj == _ffi.NULL:
+            raise ValueError("No such digest method")
+
+        sign_result = _lib.NETSCAPE_SPKI_sign(
+            self._spki, pkey._pkey, digest_obj
+        )
+        _openssl_assert(sign_result > 0)
+
+    def verify(self, key):
+        """
+        Verifies a signature on a certificate request.
+
+        :param PKey key: The public key that signature is supposedly from.
+
+        :return: ``True`` if the signature is correct.
+        :rtype: bool
+
+        :raises OpenSSL.crypto.Error: If the signature is invalid, or there was
+            a problem verifying the signature.
+        """
+        answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
+        if answer <= 0:
+            _raise_current_error()
+        return True
+
+    def b64_encode(self):
+        """
+        Generate a base64 encoded representation of this SPKI object.
+
+        :return: The base64 encoded string.
+        :rtype: :py:class:`bytes`
+        """
+        encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
+        result = _ffi.string(encoded)
+        _lib.OPENSSL_free(encoded)
+        return result
+
+    def get_pubkey(self):
+        """
+        Get the public key of this certificate.
+
+        :return: The public key.
+        :rtype: :py:class:`PKey`
+        """
+        pkey = PKey.__new__(PKey)
+        pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
+        _openssl_assert(pkey._pkey != _ffi.NULL)
+        pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
+        pkey._only_public = True
+        return pkey
+
+    def set_pubkey(self, pkey):
+        """
+        Set the public key of the certificate
+
+        :param pkey: The public key
+        :return: ``None``
+        """
+        set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
+        _openssl_assert(set_result == 1)
+
+
+class _PassphraseHelper(object):
+    def __init__(self, type, passphrase, more_args=False, truncate=False):
+        if type != FILETYPE_PEM and passphrase is not None:
+            raise ValueError(
+                "only FILETYPE_PEM key format supports encryption"
+            )
+        self._passphrase = passphrase
+        self._more_args = more_args
+        self._truncate = truncate
+        self._problems = []
+
+    @property
+    def callback(self):
+        if self._passphrase is None:
+            return _ffi.NULL
+        elif isinstance(self._passphrase, bytes) or callable(self._passphrase):
+            return _ffi.callback("pem_password_cb", self._read_passphrase)
+        else:
+            raise TypeError(
+                "Last argument must be a byte string or a callable."
+            )
+
+    @property
+    def callback_args(self):
+        if self._passphrase is None:
+            return _ffi.NULL
+        elif isinstance(self._passphrase, bytes) or callable(self._passphrase):
+            return _ffi.NULL
+        else:
+            raise TypeError(
+                "Last argument must be a byte string or a callable."
+            )
+
+    def raise_if_problem(self, exceptionType=Error):
+        if self._problems:
+
+            # Flush the OpenSSL error queue
+            try:
+                _exception_from_error_queue(exceptionType)
+            except exceptionType:
+                pass
+
+            raise self._problems.pop(0)
+
+    def _read_passphrase(self, buf, size, rwflag, userdata):
+        try:
+            if callable(self._passphrase):
+                if self._more_args:
+                    result = self._passphrase(size, rwflag, userdata)
+                else:
+                    result = self._passphrase(rwflag)
+            else:
+                result = self._passphrase
+            if not isinstance(result, bytes):
+                raise ValueError("Bytes expected")
+            if len(result) > size:
+                if self._truncate:
+                    result = result[:size]
+                else:
+                    raise ValueError(
+                        "passphrase returned by callback is too long"
+                    )
+            for i in range(len(result)):
+                buf[i] = result[i : i + 1]
+            return len(result)
+        except Exception as e:
+            self._problems.append(e)
+            return 0
+
+
+def load_publickey(type, buffer):
+    """
+    Load a public key from a buffer.
+
+    :param type: The file type (one of :data:`FILETYPE_PEM`,
+        :data:`FILETYPE_ASN1`).
+    :param buffer: The buffer the key is stored in.
+    :type buffer: A Python string object, either unicode or bytestring.
+    :return: The PKey object.
+    :rtype: :class:`PKey`
+    """
+    if isinstance(buffer, str):
+        buffer = buffer.encode("ascii")
+
+    bio = _new_mem_buf(buffer)
+
+    if type == FILETYPE_PEM:
+        evp_pkey = _lib.PEM_read_bio_PUBKEY(
+            bio, _ffi.NULL, _ffi.NULL, _ffi.NULL
+        )
+    elif type == FILETYPE_ASN1:
+        evp_pkey = _lib.d2i_PUBKEY_bio(bio, _ffi.NULL)
+    else:
+        raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
+
+    if evp_pkey == _ffi.NULL:
+        _raise_current_error()
+
+    pkey = PKey.__new__(PKey)
+    pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
+    pkey._only_public = True
+    return pkey
+
+
+def load_privatekey(type, buffer, passphrase=None):
+    """
+    Load a private key (PKey) from the string *buffer* encoded with the type
+    *type*.
+
+    :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
+    :param buffer: The buffer the key is stored in
+    :param passphrase: (optional) if encrypted PEM format, this can be
+                       either the passphrase to use, or a callback for
+                       providing the passphrase.
+
+    :return: The PKey object
+    """
+    if isinstance(buffer, str):
+        buffer = buffer.encode("ascii")
+
+    bio = _new_mem_buf(buffer)
+
+    helper = _PassphraseHelper(type, passphrase)
+    if type == FILETYPE_PEM:
+        evp_pkey = _lib.PEM_read_bio_PrivateKey(
+            bio, _ffi.NULL, helper.callback, helper.callback_args
+        )
+        helper.raise_if_problem()
+    elif type == FILETYPE_ASN1:
+        evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
+    else:
+        raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
+
+    if evp_pkey == _ffi.NULL:
+        _raise_current_error()
+
+    pkey = PKey.__new__(PKey)
+    pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
+    return pkey
+
+
+def dump_certificate_request(type, req):
+    """
+    Dump the certificate request *req* into a buffer string encoded with the
+    type *type*.
+
+    :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
+    :param req: The certificate request to dump
+    :return: The buffer with the dumped certificate request in
+    """
+    bio = _new_mem_buf()
+
+    if type == FILETYPE_PEM:
+        result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
+    elif type == FILETYPE_ASN1:
+        result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
+    elif type == FILETYPE_TEXT:
+        result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
+    else:
+        raise ValueError(
+            "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
+            "FILETYPE_TEXT"
+        )
+
+    _openssl_assert(result_code != 0)
+
+    return _bio_to_string(bio)
+
+
+def load_certificate_request(type, buffer):
+    """
+    Load a certificate request (X509Req) from the string *buffer* encoded with
+    the type *type*.
+
+    :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
+    :param buffer: The buffer the certificate request is stored in
+    :return: The X509Req object
+    """
+    if isinstance(buffer, str):
+        buffer = buffer.encode("ascii")
+
+    bio = _new_mem_buf(buffer)
+
+    if type == FILETYPE_PEM:
+        req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
+    elif type == FILETYPE_ASN1:
+        req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
+    else:
+        raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
+
+    _openssl_assert(req != _ffi.NULL)
+
+    x509req = X509Req.__new__(X509Req)
+    x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
+    return x509req
+
+
+def sign(pkey, data, digest):
+    """
+    Sign a data string using the given key and message digest.
+
+    :param pkey: PKey to sign with
+    :param data: data to be signed
+    :param digest: message digest to use
+    :return: signature
+
+    .. versionadded:: 0.11
+    """
+    data = _text_to_bytes_and_warn("data", data)
+
+    digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
+    if digest_obj == _ffi.NULL:
+        raise ValueError("No such digest method")
+
+    md_ctx = _lib.EVP_MD_CTX_new()
+    md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_free)
+
+    _lib.EVP_SignInit(md_ctx, digest_obj)
+    _lib.EVP_SignUpdate(md_ctx, data, len(data))
+
+    length = _lib.EVP_PKEY_size(pkey._pkey)
+    _openssl_assert(length > 0)
+    signature_buffer = _ffi.new("unsigned char[]", length)
+    signature_length = _ffi.new("unsigned int *")
+    final_result = _lib.EVP_SignFinal(
+        md_ctx, signature_buffer, signature_length, pkey._pkey
+    )
+    _openssl_assert(final_result == 1)
+
+    return _ffi.buffer(signature_buffer, signature_length[0])[:]
+
+
+def verify(cert, signature, data, digest):
+    """
+    Verify the signature for a data string.
+
+    :param cert: signing certificate (X509 object) corresponding to the
+        private key which generated the signature.
+    :param signature: signature returned by sign function
+    :param data: data to be verified
+    :param digest: message digest to use
+    :return: ``None`` if the signature is correct, raise exception otherwise.
+
+    .. versionadded:: 0.11
+    """
+    data = _text_to_bytes_and_warn("data", data)
+
+    digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
+    if digest_obj == _ffi.NULL:
+        raise ValueError("No such digest method")
+
+    pkey = _lib.X509_get_pubkey(cert._x509)
+    _openssl_assert(pkey != _ffi.NULL)
+    pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
+
+    md_ctx = _lib.EVP_MD_CTX_new()
+    md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_free)
+
+    _lib.EVP_VerifyInit(md_ctx, digest_obj)
+    _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
+    verify_result = _lib.EVP_VerifyFinal(
+        md_ctx, signature, len(signature), pkey
+    )
+
+    if verify_result != 1:
+        _raise_current_error()
+
+
+def dump_crl(type, crl):
+    """
+    Dump a certificate revocation list to a buffer.
+
+    :param type: The file type (one of ``FILETYPE_PEM``, ``FILETYPE_ASN1``, or
+        ``FILETYPE_TEXT``).
+    :param CRL crl: The CRL to dump.
+
+    :return: The buffer with the CRL.
+    :rtype: bytes
+    """
+    bio = _new_mem_buf()
+
+    if type == FILETYPE_PEM:
+        ret = _lib.PEM_write_bio_X509_CRL(bio, crl._crl)
+    elif type == FILETYPE_ASN1:
+        ret = _lib.i2d_X509_CRL_bio(bio, crl._crl)
+    elif type == FILETYPE_TEXT:
+        ret = _lib.X509_CRL_print(bio, crl._crl)
+    else:
+        raise ValueError(
+            "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
+            "FILETYPE_TEXT"
+        )
+
+    _openssl_assert(ret == 1)
+    return _bio_to_string(bio)
+
+
+def load_crl(type, buffer):
+    """
+    Load Certificate Revocation List (CRL) data from a string *buffer*.
+    *buffer* encoded with the type *type*.
+
+    :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
+    :param buffer: The buffer the CRL is stored in
+
+    :return: The PKey object
+    """
+    if isinstance(buffer, str):
+        buffer = buffer.encode("ascii")
+
+    bio = _new_mem_buf(buffer)
+
+    if type == FILETYPE_PEM:
+        crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
+    elif type == FILETYPE_ASN1:
+        crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
+    else:
+        raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
+
+    if crl == _ffi.NULL:
+        _raise_current_error()
+
+    result = CRL.__new__(CRL)
+    result._crl = _ffi.gc(crl, _lib.X509_CRL_free)
+    return result
+
+
+def load_pkcs7_data(type, buffer):
+    """
+    Load pkcs7 data from the string *buffer* encoded with the type
+    *type*.
+
+    :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
+    :param buffer: The buffer with the pkcs7 data.
+    :return: The PKCS7 object
+    """
+    if isinstance(buffer, str):
+        buffer = buffer.encode("ascii")
+
+    bio = _new_mem_buf(buffer)
+
+    if type == FILETYPE_PEM:
+        pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
+    elif type == FILETYPE_ASN1:
+        pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
+    else:
+        raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
+
+    if pkcs7 == _ffi.NULL:
+        _raise_current_error()
+
+    pypkcs7 = PKCS7.__new__(PKCS7)
+    pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
+    return pypkcs7
+
+
+load_pkcs7_data = utils.deprecated(
+    load_pkcs7_data,
+    __name__,
+    (
+        "PKCS#7 support in pyOpenSSL is deprecated. You should use the APIs "
+        "in cryptography."
+    ),
+    DeprecationWarning,
+)
+
+
+def load_pkcs12(buffer, passphrase=None):
+    """
+    Load pkcs12 data from the string *buffer*. If the pkcs12 structure is
+    encrypted, a *passphrase* must be included.  The MAC is always
+    checked and thus required.
+
+    See also the man page for the C function :py:func:`PKCS12_parse`.
+
+    :param buffer: The buffer the certificate is stored in
+    :param passphrase: (Optional) The password to decrypt the PKCS12 lump
+    :returns: The PKCS12 object
+    """
+    passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
+
+    if isinstance(buffer, str):
+        buffer = buffer.encode("ascii")
+
+    bio = _new_mem_buf(buffer)
+
+    # Use null passphrase if passphrase is None or empty string. With PKCS#12
+    # password based encryption no password and a zero length password are two
+    # different things, but OpenSSL implementation will try both to figure out
+    # which one works.
+    if not passphrase:
+        passphrase = _ffi.NULL
+
+    p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
+    if p12 == _ffi.NULL:
+        _raise_current_error()
+    p12 = _ffi.gc(p12, _lib.PKCS12_free)
+
+    pkey = _ffi.new("EVP_PKEY**")
+    cert = _ffi.new("X509**")
+    cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
+
+    parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
+    if not parse_result:
+        _raise_current_error()
+
+    cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
+
+    # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
+    # queue for no particular reason.  This error isn't interesting to anyone
+    # outside this function.  It's not even interesting to us.  Get rid of it.
+    try:
+        _raise_current_error()
+    except Error:
+        pass
+
+    if pkey[0] == _ffi.NULL:
+        pykey = None
+    else:
+        pykey = PKey.__new__(PKey)
+        pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
+
+    if cert[0] == _ffi.NULL:
+        pycert = None
+        friendlyname = None
+    else:
+        pycert = X509._from_raw_x509_ptr(cert[0])
+
+        friendlyname_length = _ffi.new("int*")
+        friendlyname_buffer = _lib.X509_alias_get0(
+            cert[0], friendlyname_length
+        )
+        friendlyname = _ffi.buffer(
+            friendlyname_buffer, friendlyname_length[0]
+        )[:]
+        if friendlyname_buffer == _ffi.NULL:
+            friendlyname = None
+
+    pycacerts = []
+    for i in range(_lib.sk_X509_num(cacerts)):
+        x509 = _lib.sk_X509_value(cacerts, i)
+        pycacert = X509._from_raw_x509_ptr(x509)
+        pycacerts.append(pycacert)
+    if not pycacerts:
+        pycacerts = None
+
+    pkcs12 = PKCS12.__new__(PKCS12)
+    pkcs12._pkey = pykey
+    pkcs12._cert = pycert
+    pkcs12._cacerts = pycacerts
+    pkcs12._friendlyname = friendlyname
+    return pkcs12
+
+
+load_pkcs12 = utils.deprecated(
+    load_pkcs12,
+    __name__,
+    (
+        "PKCS#12 support in pyOpenSSL is deprecated. You should use the APIs "
+        "in cryptography."
+    ),
+    DeprecationWarning,
+)
+
+
+# There are no direct unit tests for this initialization.  It is tested
+# indirectly since it is necessary for functions like dump_privatekey when
+# using encryption.
+#
+# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
+# and some other similar tests may fail without this (though they may not if
+# the Python runtime has already done some initialization of the underlying
+# OpenSSL library (and is linked against the same one that cryptography is
+# using)).
+_lib.OpenSSL_add_all_algorithms()
+
+# This is similar but exercised mainly by exception_from_error_queue.  It calls
+# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
+_lib.SSL_load_error_strings()
+
+
+# Set the default string mask to match OpenSSL upstream (since 2005) and
+# RFC5280 recommendations.
+_lib.ASN1_STRING_set_default_mask_asc(b"utf8only")

+ 42 - 0
rest_venv/Lib/site-packages/OpenSSL/debug.py

@@ -0,0 +1,42 @@
+from __future__ import print_function
+
+import ssl
+import sys
+
+import OpenSSL.SSL
+import cffi
+import cryptography
+
+from . import version
+
+
+_env_info = """\
+pyOpenSSL: {pyopenssl}
+cryptography: {cryptography}
+cffi: {cffi}
+cryptography's compiled against OpenSSL: {crypto_openssl_compile}
+cryptography's linked OpenSSL: {crypto_openssl_link}
+Python's OpenSSL: {python_openssl}
+Python executable: {python}
+Python version: {python_version}
+Platform: {platform}
+sys.path: {sys_path}""".format(
+    pyopenssl=version.__version__,
+    crypto_openssl_compile=OpenSSL._util.ffi.string(
+        OpenSSL._util.lib.OPENSSL_VERSION_TEXT,
+    ).decode("ascii"),
+    crypto_openssl_link=OpenSSL.SSL.SSLeay_version(
+        OpenSSL.SSL.SSLEAY_VERSION
+    ).decode("ascii"),
+    python_openssl=getattr(ssl, "OPENSSL_VERSION", "n/a"),
+    cryptography=cryptography.__version__,
+    cffi=cffi.__version__,
+    python=sys.executable,
+    python_version=sys.version,
+    platform=sys.platform,
+    sys_path=sys.path,
+)
+
+
+if __name__ == "__main__":
+    print(_env_info)

+ 40 - 0
rest_venv/Lib/site-packages/OpenSSL/rand.py

@@ -0,0 +1,40 @@
+"""
+PRNG management routines, thin wrappers.
+"""
+
+from OpenSSL._util import lib as _lib
+
+
+def add(buffer, entropy):
+    """
+    Mix bytes from *string* into the PRNG state.
+
+    The *entropy* argument is (the lower bound of) an estimate of how much
+    randomness is contained in *string*, measured in bytes.
+
+    For more information, see e.g. :rfc:`1750`.
+
+    This function is only relevant if you are forking Python processes and
+    need to reseed the CSPRNG after fork.
+
+    :param buffer: Buffer with random data.
+    :param entropy: The entropy (in bytes) measurement of the buffer.
+
+    :return: :obj:`None`
+    """
+    if not isinstance(buffer, bytes):
+        raise TypeError("buffer must be a byte string")
+
+    if not isinstance(entropy, int):
+        raise TypeError("entropy must be an integer")
+
+    _lib.RAND_add(buffer, len(buffer), entropy)
+
+
+def status():
+    """
+    Check whether the PRNG has been seeded with enough data.
+
+    :return: 1 if the PRNG is seeded enough, 0 otherwise.
+    """
+    return _lib.RAND_status()

+ 28 - 0
rest_venv/Lib/site-packages/OpenSSL/version.py

@@ -0,0 +1,28 @@
+# Copyright (C) AB Strakt
+# Copyright (C) Jean-Paul Calderone
+# See LICENSE for details.
+
+"""
+pyOpenSSL - A simple wrapper around the OpenSSL library
+"""
+
+__all__ = [
+    "__author__",
+    "__copyright__",
+    "__email__",
+    "__license__",
+    "__summary__",
+    "__title__",
+    "__uri__",
+    "__version__",
+]
+
+__version__ = "22.0.0"
+
+__title__ = "pyOpenSSL"
+__uri__ = "https://pyopenssl.org/"
+__summary__ = "Python wrapper module around the OpenSSL library"
+__author__ = "The pyOpenSSL developers"
+__email__ = "cryptography-dev@python.org"
+__license__ = "Apache License, Version 2.0"
+__copyright__ = "Copyright 2001-2020 {0}".format(__author__)

+ 0 - 1
rest_venv/Lib/site-packages/PyJWT-2.4.0.dist-info/RECORD

@@ -3,7 +3,6 @@ PyJWT-2.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvF
 PyJWT-2.4.0.dist-info/LICENSE,sha256=eXp6ICMdTEM-nxkR2xcx0GtYKLmPSZgZoDT3wPVvXOU,1085
 PyJWT-2.4.0.dist-info/METADATA,sha256=4m9Z-JOCU4d6e0DRC93ixVAkX5YbyQuyPWX7vMUq2xo,4075
 PyJWT-2.4.0.dist-info/RECORD,,
-PyJWT-2.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
 PyJWT-2.4.0.dist-info/WHEEL,sha256=EVRjI69F5qVjm_YgqcTXPnTAv3BfSUr0WVAHuSP3Xoo,92
 PyJWT-2.4.0.dist-info/top_level.txt,sha256=RP5DHNyJbMq2ka0FmfTgoSaQzh7e3r5XuCWCO8a00k8,4
 jwt/__init__.py,sha256=tynTaXmXC3wgkBntMyMYmmF9CL7WbE-OhHl3MMv18GQ,1548

+ 22 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/__init__.py

@@ -0,0 +1,22 @@
+from .jwt_manager import JWTManager
+from .utils import create_access_token
+from .utils import create_refresh_token
+from .utils import current_user
+from .utils import decode_token
+from .utils import get_csrf_token
+from .utils import get_current_user
+from .utils import get_jti
+from .utils import get_jwt
+from .utils import get_jwt_header
+from .utils import get_jwt_identity
+from .utils import get_jwt_request_location
+from .utils import get_unverified_jwt_headers
+from .utils import set_access_cookies
+from .utils import set_refresh_cookies
+from .utils import unset_access_cookies
+from .utils import unset_jwt_cookies
+from .utils import unset_refresh_cookies
+from .view_decorators import jwt_required
+from .view_decorators import verify_jwt_in_request
+
+__version__ = "4.4.1"

TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/__init__.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/config.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/default_callbacks.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/exceptions.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/internal_utils.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/jwt_manager.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/tokens.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/typing.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/utils.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/flask_jwt_extended/__pycache__/view_decorators.cpython-39.pyc


+ 314 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/config.py

@@ -0,0 +1,314 @@
+from datetime import datetime
+from datetime import timedelta
+from datetime import timezone
+from typing import Iterable
+from typing import List
+from typing import Optional
+from typing import Sequence
+from typing import Type
+from typing import Union
+
+from flask import current_app
+from flask.json import JSONEncoder
+from jwt.algorithms import requires_cryptography
+
+from flask_jwt_extended.typing import ExpiresDelta
+
+
+class _Config(object):
+    """
+    Helper object for accessing and verifying options in this extension. This
+    is meant for internal use of the application; modifying config options
+    should be done with flasks ```app.config```.
+
+    Default values for the configuration options are set in the jwt_manager
+    object. All of these values are read only. This is simply a loose wrapper
+    with some helper functionality for flasks `app.config`.
+    """
+
+    @property
+    def is_asymmetric(self) -> bool:
+        return self.algorithm in requires_cryptography
+
+    @property
+    def encode_key(self) -> str:
+        return self._private_key if self.is_asymmetric else self._secret_key
+
+    @property
+    def decode_key(self) -> str:
+        return self._public_key if self.is_asymmetric else self._secret_key
+
+    @property
+    def token_location(self) -> Sequence[str]:
+        locations = current_app.config["JWT_TOKEN_LOCATION"]
+        if isinstance(locations, str):
+            locations = (locations,)
+        elif not isinstance(locations, Iterable):
+            raise RuntimeError("JWT_TOKEN_LOCATION must be a sequence or a set")
+        elif not locations:
+            raise RuntimeError(
+                "JWT_TOKEN_LOCATION must contain at least one "
+                'of "headers", "cookies", "query_string", or "json"'
+            )
+        for location in locations:
+            if location not in ("headers", "cookies", "query_string", "json"):
+                raise RuntimeError(
+                    "JWT_TOKEN_LOCATION can only contain "
+                    '"headers", "cookies", "query_string", or "json"'
+                )
+        return locations
+
+    @property
+    def jwt_in_cookies(self) -> bool:
+        return "cookies" in self.token_location
+
+    @property
+    def jwt_in_headers(self) -> bool:
+        return "headers" in self.token_location
+
+    @property
+    def jwt_in_query_string(self) -> bool:
+        return "query_string" in self.token_location
+
+    @property
+    def jwt_in_json(self) -> bool:
+        return "json" in self.token_location
+
+    @property
+    def header_name(self) -> str:
+        name = current_app.config["JWT_HEADER_NAME"]
+        if not name:
+            raise RuntimeError("JWT_ACCESS_HEADER_NAME cannot be empty")
+        return name
+
+    @property
+    def header_type(self) -> str:
+        return current_app.config["JWT_HEADER_TYPE"]
+
+    @property
+    def query_string_name(self) -> str:
+        return current_app.config["JWT_QUERY_STRING_NAME"]
+
+    @property
+    def query_string_value_prefix(self) -> str:
+        return current_app.config["JWT_QUERY_STRING_VALUE_PREFIX"]
+
+    @property
+    def access_cookie_name(self) -> str:
+        return current_app.config["JWT_ACCESS_COOKIE_NAME"]
+
+    @property
+    def refresh_cookie_name(self) -> str:
+        return current_app.config["JWT_REFRESH_COOKIE_NAME"]
+
+    @property
+    def access_cookie_path(self) -> str:
+        return current_app.config["JWT_ACCESS_COOKIE_PATH"]
+
+    @property
+    def refresh_cookie_path(self) -> str:
+        return current_app.config["JWT_REFRESH_COOKIE_PATH"]
+
+    @property
+    def cookie_secure(self) -> bool:
+        return current_app.config["JWT_COOKIE_SECURE"]
+
+    @property
+    def cookie_domain(self) -> str:
+        return current_app.config["JWT_COOKIE_DOMAIN"]
+
+    @property
+    def session_cookie(self) -> bool:
+        return current_app.config["JWT_SESSION_COOKIE"]
+
+    @property
+    def cookie_samesite(self) -> str:
+        return current_app.config["JWT_COOKIE_SAMESITE"]
+
+    @property
+    def json_key(self) -> str:
+        return current_app.config["JWT_JSON_KEY"]
+
+    @property
+    def refresh_json_key(self) -> str:
+        return current_app.config["JWT_REFRESH_JSON_KEY"]
+
+    @property
+    def csrf_protect(self) -> bool:
+        return self.jwt_in_cookies and current_app.config["JWT_COOKIE_CSRF_PROTECT"]
+
+    @property
+    def csrf_request_methods(self) -> Iterable[str]:
+        return current_app.config["JWT_CSRF_METHODS"]
+
+    @property
+    def csrf_in_cookies(self) -> bool:
+        return current_app.config["JWT_CSRF_IN_COOKIES"]
+
+    @property
+    def access_csrf_cookie_name(self) -> str:
+        return current_app.config["JWT_ACCESS_CSRF_COOKIE_NAME"]
+
+    @property
+    def refresh_csrf_cookie_name(self) -> str:
+        return current_app.config["JWT_REFRESH_CSRF_COOKIE_NAME"]
+
+    @property
+    def access_csrf_cookie_path(self) -> str:
+        return current_app.config["JWT_ACCESS_CSRF_COOKIE_PATH"]
+
+    @property
+    def refresh_csrf_cookie_path(self) -> str:
+        return current_app.config["JWT_REFRESH_CSRF_COOKIE_PATH"]
+
+    @property
+    def access_csrf_header_name(self) -> str:
+        return current_app.config["JWT_ACCESS_CSRF_HEADER_NAME"]
+
+    @property
+    def refresh_csrf_header_name(self) -> str:
+        return current_app.config["JWT_REFRESH_CSRF_HEADER_NAME"]
+
+    @property
+    def csrf_check_form(self) -> bool:
+        return current_app.config["JWT_CSRF_CHECK_FORM"]
+
+    @property
+    def access_csrf_field_name(self) -> str:
+        return current_app.config["JWT_ACCESS_CSRF_FIELD_NAME"]
+
+    @property
+    def refresh_csrf_field_name(self) -> str:
+        return current_app.config["JWT_REFRESH_CSRF_FIELD_NAME"]
+
+    @property
+    def access_expires(self) -> ExpiresDelta:
+        delta = current_app.config["JWT_ACCESS_TOKEN_EXPIRES"]
+        if type(delta) is int:
+            delta = timedelta(seconds=delta)
+        if delta is not False:
+            try:
+                # Basically runtime typechecking. Probably a better way to do
+                # this with proper type checking
+                delta + datetime.now(timezone.utc)
+            except TypeError as e:
+                err = (
+                    "must be able to add JWT_ACCESS_TOKEN_EXPIRES to datetime.datetime"
+                )
+                raise RuntimeError(err) from e
+        return delta
+
+    @property
+    def refresh_expires(self) -> ExpiresDelta:
+        delta = current_app.config["JWT_REFRESH_TOKEN_EXPIRES"]
+        if type(delta) is int:
+            delta = timedelta(seconds=delta)
+        if delta is not False:
+            # Basically runtime typechecking. Probably a better way to do
+            # this with proper type checking
+            try:
+                delta + datetime.now(timezone.utc)
+            except TypeError as e:
+                err = (
+                    "must be able to add JWT_REFRESH_TOKEN_EXPIRES to datetime.datetime"
+                )
+                raise RuntimeError(err) from e
+        return delta
+
+    @property
+    def algorithm(self) -> str:
+        return current_app.config["JWT_ALGORITHM"]
+
+    @property
+    def decode_algorithms(self) -> List[str]:
+        algorithms = current_app.config["JWT_DECODE_ALGORITHMS"]
+        if not algorithms:
+            return [self.algorithm]
+        if self.algorithm not in algorithms:
+            algorithms.append(self.algorithm)
+        return algorithms
+
+    @property
+    def _secret_key(self) -> str:
+        key = current_app.config["JWT_SECRET_KEY"]
+        if not key:
+            key = current_app.config.get("SECRET_KEY", None)
+            if not key:
+                raise RuntimeError(
+                    "JWT_SECRET_KEY or flask SECRET_KEY "
+                    "must be set when using symmetric "
+                    'algorithm "{}"'.format(self.algorithm)
+                )
+        return key
+
+    @property
+    def _public_key(self) -> str:
+        key = current_app.config["JWT_PUBLIC_KEY"]
+        if not key:
+            raise RuntimeError(
+                "JWT_PUBLIC_KEY must be set to use "
+                "asymmetric cryptography algorithm "
+                '"{}"'.format(self.algorithm)
+            )
+        return key
+
+    @property
+    def _private_key(self) -> str:
+        key = current_app.config["JWT_PRIVATE_KEY"]
+        if not key:
+            raise RuntimeError(
+                "JWT_PRIVATE_KEY must be set to use "
+                "asymmetric cryptography algorithm "
+                '"{}"'.format(self.algorithm)
+            )
+        return key
+
+    @property
+    def cookie_max_age(self) -> Optional[int]:
+        # Returns the appropiate value for max_age for flask set_cookies. If
+        # session cookie is true, return None, otherwise return a number of
+        # seconds 1 year in the future
+        return None if self.session_cookie else 31540000  # 1 year
+
+    @property
+    def identity_claim_key(self) -> str:
+        return current_app.config["JWT_IDENTITY_CLAIM"]
+
+    @property
+    def exempt_methods(self) -> Iterable[str]:
+        return {"OPTIONS"}
+
+    @property
+    def error_msg_key(self) -> str:
+        return current_app.config["JWT_ERROR_MESSAGE_KEY"]
+
+    @property
+    def json_encoder(self) -> Type[JSONEncoder]:
+        return current_app.json_encoder
+
+    @property
+    def decode_audience(self) -> Union[str, Iterable[str]]:
+        return current_app.config["JWT_DECODE_AUDIENCE"]
+
+    @property
+    def encode_audience(self) -> Union[str, Iterable[str]]:
+        return current_app.config["JWT_ENCODE_AUDIENCE"]
+
+    @property
+    def encode_issuer(self) -> str:
+        return current_app.config["JWT_ENCODE_ISSUER"]
+
+    @property
+    def decode_issuer(self) -> str:
+        return current_app.config["JWT_DECODE_ISSUER"]
+
+    @property
+    def leeway(self) -> int:
+        return current_app.config["JWT_DECODE_LEEWAY"]
+
+    @property
+    def encode_nbf(self) -> bool:
+        return current_app.config["JWT_ENCODE_NBF"]
+
+
+config = _Config()

+ 161 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/default_callbacks.py

@@ -0,0 +1,161 @@
+"""
+These are the default methods implementations that are used in this extension.
+All of these can be updated on an app by app basis using the JWTManager
+loader decorators. For further information, check out the following links:
+
+http://flask-jwt-extended.readthedocs.io/en/latest/changing_default_behavior.html
+http://flask-jwt-extended.readthedocs.io/en/latest/tokens_from_complex_object.html
+"""
+from http import HTTPStatus
+from typing import Any
+
+from flask import jsonify
+from flask.typing import ResponseReturnValue
+
+from flask_jwt_extended.config import config
+
+
+def default_additional_claims_callback(userdata: Any) -> dict:
+    """
+    By default, we add no additional claims to the access tokens.
+
+    :param userdata: data passed in as the ```identity``` argument to the
+                     ```create_access_token``` and ```create_refresh_token```
+                     functions
+    """
+    return {}
+
+
+def default_blocklist_callback(jwt_headers: dict, jwt_data: dict) -> bool:
+    return False
+
+
+def default_jwt_headers_callback(default_headers) -> dict:
+    """
+    By default header typically consists of two parts: the type of the token,
+    which is JWT, and the signing algorithm being used, such as HMAC SHA256
+    or RSA. But we don't set the default header here we set it as empty which
+    further by default set while encoding the token
+    :return: default we set None here
+    """
+    return {}
+
+
+def default_user_identity_callback(userdata: Any) -> Any:
+    """
+    By default, we use the passed in object directly as the jwt identity.
+    See this for additional info:
+
+    :param userdata: data passed in as the ```identity``` argument to the
+                     ```create_access_token``` and ```create_refresh_token```
+                     functions
+    """
+    return userdata
+
+
+def default_expired_token_callback(
+    _expired_jwt_header: dict, _expired_jwt_data: dict
+) -> ResponseReturnValue:
+    """
+    By default, if an expired token attempts to access a protected endpoint,
+    we return a generic error message with a 401 status
+    """
+    return jsonify({config.error_msg_key: "Token has expired"}), HTTPStatus.UNAUTHORIZED
+
+
+def default_invalid_token_callback(error_string: str) -> ResponseReturnValue:
+    """
+    By default, if an invalid token attempts to access a protected endpoint, we
+    return the error string for why it is not valid with a 422 status code
+
+    :param error_string: String indicating why the token is invalid
+    """
+    return (
+        jsonify({config.error_msg_key: error_string}),
+        HTTPStatus.UNPROCESSABLE_ENTITY,
+    )
+
+
+def default_unauthorized_callback(error_string: str) -> ResponseReturnValue:
+    """
+    By default, if a protected endpoint is accessed without a JWT, we return
+    the error string indicating why this is unauthorized, with a 401 status code
+
+    :param error_string: String indicating why this request is unauthorized
+    """
+    return jsonify({config.error_msg_key: error_string}), HTTPStatus.UNAUTHORIZED
+
+
+def default_needs_fresh_token_callback(
+    jwt_header: dict, jwt_data: dict
+) -> ResponseReturnValue:
+    """
+    By default, if a non-fresh jwt is used to access a ```fresh_jwt_required```
+    endpoint, we return a general error message with a 401 status code
+    """
+    return (
+        jsonify({config.error_msg_key: "Fresh token required"}),
+        HTTPStatus.UNAUTHORIZED,
+    )
+
+
+def default_revoked_token_callback(
+    jwt_header: dict, jwt_data: dict
+) -> ResponseReturnValue:
+    """
+    By default, if a revoked token is used to access a protected endpoint, we
+    return a general error message with a 401 status code
+    """
+    return (
+        jsonify({config.error_msg_key: "Token has been revoked"}),
+        HTTPStatus.UNAUTHORIZED,
+    )
+
+
+def default_user_lookup_error_callback(
+    _jwt_header: dict, jwt_data: dict
+) -> ResponseReturnValue:
+    """
+    By default, if a user_lookup callback is defined and the callback
+    function returns None, we return a general error message with a 401
+    status code
+    """
+    identity = jwt_data[config.identity_claim_key]
+    result = {config.error_msg_key: f"Error loading the user {identity}"}
+    return jsonify(result), HTTPStatus.UNAUTHORIZED
+
+
+def default_token_verification_callback(_jwt_header: dict, _jwt_data: dict) -> bool:
+    """
+    By default, we do not do any verification of the user claims.
+    """
+    return True
+
+
+def default_token_verification_failed_callback(
+    _jwt_header: dict, _jwt_data: dict
+) -> ResponseReturnValue:
+    """
+    By default, if the user claims verification failed, we return a generic
+    error message with a 400 status code
+    """
+    return (
+        jsonify({config.error_msg_key: "User claims verification failed"}),
+        HTTPStatus.BAD_REQUEST,
+    )
+
+
+def default_decode_key_callback(jwt_header: dict, jwt_data: dict) -> str:
+    """
+    By default, the decode key specified via the JWT_SECRET_KEY or
+    JWT_PUBLIC_KEY settings will be used to decode all tokens
+    """
+    return config.decode_key
+
+
+def default_encode_key_callback(identity: Any) -> str:
+    """
+    By default, the encode key specified via the JWT_SECRET_KEY or
+    JWT_PRIVATE_KEY settings will be used to encode all tokens
+    """
+    return config.encode_key

+ 102 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/exceptions.py

@@ -0,0 +1,102 @@
+class JWTExtendedException(Exception):
+    """
+    Base except which all flask_jwt_extended errors extend
+    """
+
+    pass
+
+
+class JWTDecodeError(JWTExtendedException):
+    """
+    An error decoding a JWT
+    """
+
+    pass
+
+
+class InvalidHeaderError(JWTExtendedException):
+    """
+    An error getting header information from a request
+    """
+
+    pass
+
+
+class InvalidQueryParamError(JWTExtendedException):
+    """
+    An error when a query string param is not in the correct format
+    """
+
+    pass
+
+
+class NoAuthorizationError(JWTExtendedException):
+    """
+    An error raised when no authorization token was found in a protected endpoint
+    """
+
+    pass
+
+
+class CSRFError(JWTExtendedException):
+    """
+    An error with CSRF protection
+    """
+
+    pass
+
+
+class WrongTokenError(JWTExtendedException):
+    """
+    Error raised when attempting to use a refresh token to access an endpoint
+    or vice versa
+    """
+
+    pass
+
+
+class RevokedTokenError(JWTExtendedException):
+    """
+    Error raised when a revoked token attempt to access a protected endpoint
+    """
+
+    def __init__(self, jwt_header: dict, jwt_data: dict) -> None:
+        super().__init__("Token has been revoked")
+        self.jwt_header = jwt_header
+        self.jwt_data = jwt_data
+
+
+class FreshTokenRequired(JWTExtendedException):
+    """
+    Error raised when a valid, non-fresh JWT attempt to access an endpoint
+    protected by fresh_jwt_required
+    """
+
+    def __init__(self, message, jwt_header: dict, jwt_data: dict) -> None:
+        super().__init__(message)
+        self.jwt_header = jwt_header
+        self.jwt_data = jwt_data
+
+
+class UserLookupError(JWTExtendedException):
+    """
+    Error raised when a user_lookup callback function returns None, indicating
+    that it cannot or will not load a user for the given identity.
+    """
+
+    def __init__(self, message, jwt_header: dict, jwt_data: dict) -> None:
+        super().__init__(message)
+        self.jwt_header = jwt_header
+        self.jwt_data = jwt_data
+
+
+class UserClaimsVerificationError(JWTExtendedException):
+    """
+    Error raised when the claims_verification_callback function returns False,
+    indicating that the expected user claims are invalid
+    """
+
+    def __init__(self, message, jwt_header: dict, jwt_data: dict) -> None:
+        super().__init__(message)
+        self.jwt_header = jwt_header
+        self.jwt_data = jwt_data

+ 50 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/internal_utils.py

@@ -0,0 +1,50 @@
+from typing import Any
+
+from flask import current_app
+
+from flask_jwt_extended import JWTManager
+from flask_jwt_extended.exceptions import RevokedTokenError
+from flask_jwt_extended.exceptions import UserClaimsVerificationError
+from flask_jwt_extended.exceptions import WrongTokenError
+
+
+def get_jwt_manager() -> JWTManager:
+    try:
+        return current_app.extensions["flask-jwt-extended"]
+    except KeyError:  # pragma: no cover
+        raise RuntimeError(
+            "You must initialize a JWTManager with this flask "
+            "application before using this method"
+        ) from None
+
+
+def has_user_lookup() -> bool:
+    jwt_manager = get_jwt_manager()
+    return jwt_manager._user_lookup_callback is not None
+
+
+def user_lookup(*args, **kwargs) -> Any:
+    jwt_manager = get_jwt_manager()
+    return jwt_manager._user_lookup_callback and jwt_manager._user_lookup_callback(
+        *args, **kwargs
+    )
+
+
+def verify_token_type(decoded_token: dict, refresh: bool) -> None:
+    if not refresh and decoded_token["type"] == "refresh":
+        raise WrongTokenError("Only non-refresh tokens are allowed")
+    elif refresh and decoded_token["type"] != "refresh":
+        raise WrongTokenError("Only refresh tokens are allowed")
+
+
+def verify_token_not_blocklisted(jwt_header: dict, jwt_data: dict) -> None:
+    jwt_manager = get_jwt_manager()
+    if jwt_manager._token_in_blocklist_callback(jwt_header, jwt_data):
+        raise RevokedTokenError(jwt_header, jwt_data)
+
+
+def custom_verification_for_token(jwt_header: dict, jwt_data: dict) -> None:
+    jwt_manager = get_jwt_manager()
+    if not jwt_manager._token_verification_callback(jwt_header, jwt_data):
+        error_msg = "User claims verification failed"
+        raise UserClaimsVerificationError(error_msg, jwt_header, jwt_data)

+ 548 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/jwt_manager.py

@@ -0,0 +1,548 @@
+import datetime
+from typing import Any
+from typing import Callable
+from typing import Optional
+
+import jwt
+from flask import Flask
+from jwt import DecodeError
+from jwt import ExpiredSignatureError
+from jwt import InvalidAudienceError
+from jwt import InvalidIssuerError
+from jwt import InvalidTokenError
+from jwt import MissingRequiredClaimError
+
+from flask_jwt_extended.config import config
+from flask_jwt_extended.default_callbacks import default_additional_claims_callback
+from flask_jwt_extended.default_callbacks import default_blocklist_callback
+from flask_jwt_extended.default_callbacks import default_decode_key_callback
+from flask_jwt_extended.default_callbacks import default_encode_key_callback
+from flask_jwt_extended.default_callbacks import default_expired_token_callback
+from flask_jwt_extended.default_callbacks import default_invalid_token_callback
+from flask_jwt_extended.default_callbacks import default_jwt_headers_callback
+from flask_jwt_extended.default_callbacks import default_needs_fresh_token_callback
+from flask_jwt_extended.default_callbacks import default_revoked_token_callback
+from flask_jwt_extended.default_callbacks import default_token_verification_callback
+from flask_jwt_extended.default_callbacks import (
+    default_token_verification_failed_callback,
+)
+from flask_jwt_extended.default_callbacks import default_unauthorized_callback
+from flask_jwt_extended.default_callbacks import default_user_identity_callback
+from flask_jwt_extended.default_callbacks import default_user_lookup_error_callback
+from flask_jwt_extended.exceptions import CSRFError
+from flask_jwt_extended.exceptions import FreshTokenRequired
+from flask_jwt_extended.exceptions import InvalidHeaderError
+from flask_jwt_extended.exceptions import InvalidQueryParamError
+from flask_jwt_extended.exceptions import JWTDecodeError
+from flask_jwt_extended.exceptions import NoAuthorizationError
+from flask_jwt_extended.exceptions import RevokedTokenError
+from flask_jwt_extended.exceptions import UserClaimsVerificationError
+from flask_jwt_extended.exceptions import UserLookupError
+from flask_jwt_extended.exceptions import WrongTokenError
+from flask_jwt_extended.tokens import _decode_jwt
+from flask_jwt_extended.tokens import _encode_jwt
+from flask_jwt_extended.typing import ExpiresDelta
+
+
+class JWTManager(object):
+    """
+    An object used to hold JWT settings and callback functions for the
+    Flask-JWT-Extended extension.
+
+    Instances of :class:`JWTManager` are *not* bound to specific apps, so
+    you can create one in the main body of your code and then bind it
+    to your app in a factory function.
+    """
+
+    def __init__(self, app: Flask = None) -> None:
+        """
+        Create the JWTManager instance. You can either pass a flask application
+        in directly here to register this extension with the flask app, or
+        call init_app after creating this object (in a factory pattern).
+
+        :param app:
+            The Flask Application object
+        """
+        # Register the default error handler callback methods. These can be
+        # overridden with the appropriate loader decorators
+        self._decode_key_callback = default_decode_key_callback
+        self._encode_key_callback = default_encode_key_callback
+        self._expired_token_callback = default_expired_token_callback
+        self._invalid_token_callback = default_invalid_token_callback
+        self._jwt_additional_header_callback = default_jwt_headers_callback
+        self._needs_fresh_token_callback = default_needs_fresh_token_callback
+        self._revoked_token_callback = default_revoked_token_callback
+        self._token_in_blocklist_callback = default_blocklist_callback
+        self._token_verification_callback = default_token_verification_callback
+        self._unauthorized_callback = default_unauthorized_callback
+        self._user_claims_callback = default_additional_claims_callback
+        self._user_identity_callback = default_user_identity_callback
+        self._user_lookup_callback: Optional[Callable] = None
+        self._user_lookup_error_callback = default_user_lookup_error_callback
+        self._token_verification_failed_callback = (
+            default_token_verification_failed_callback
+        )
+
+        # Register this extension with the flask app now (if it is provided)
+        if app is not None:
+            self.init_app(app)
+
+    def init_app(self, app: Flask) -> None:
+        """
+        Register this extension with the flask app.
+
+        :param app:
+            The Flask Application object
+        """
+        # Save this so we can use it later in the extension
+        if not hasattr(app, "extensions"):  # pragma: no cover
+            app.extensions = {}
+        app.extensions["flask-jwt-extended"] = self
+
+        # Set all the default configurations for this extension
+        self._set_default_configuration_options(app)
+        self._set_error_handler_callbacks(app)
+
+    def _set_error_handler_callbacks(self, app: Flask) -> None:
+        @app.errorhandler(CSRFError)
+        def handle_csrf_error(e):
+            return self._unauthorized_callback(str(e))
+
+        @app.errorhandler(DecodeError)
+        def handle_decode_error(e):
+            return self._invalid_token_callback(str(e))
+
+        @app.errorhandler(ExpiredSignatureError)
+        def handle_expired_error(e):
+            return self._expired_token_callback(e.jwt_header, e.jwt_data)
+
+        @app.errorhandler(FreshTokenRequired)
+        def handle_fresh_token_required(e):
+            return self._needs_fresh_token_callback(e.jwt_header, e.jwt_data)
+
+        @app.errorhandler(MissingRequiredClaimError)
+        def handle_missing_required_claim_error(e):
+            return self._invalid_token_callback(str(e))
+
+        @app.errorhandler(InvalidAudienceError)
+        def handle_invalid_audience_error(e):
+            return self._invalid_token_callback(str(e))
+
+        @app.errorhandler(InvalidIssuerError)
+        def handle_invalid_issuer_error(e):
+            return self._invalid_token_callback(str(e))
+
+        @app.errorhandler(InvalidHeaderError)
+        def handle_invalid_header_error(e):
+            return self._invalid_token_callback(str(e))
+
+        @app.errorhandler(InvalidTokenError)
+        def handle_invalid_token_error(e):
+            return self._invalid_token_callback(str(e))
+
+        @app.errorhandler(JWTDecodeError)
+        def handle_jwt_decode_error(e):
+            return self._invalid_token_callback(str(e))
+
+        @app.errorhandler(NoAuthorizationError)
+        def handle_auth_error(e):
+            return self._unauthorized_callback(str(e))
+
+        @app.errorhandler(InvalidQueryParamError)
+        def handle_invalid_query_param_error(e):
+            return self._invalid_token_callback(str(e))
+
+        @app.errorhandler(RevokedTokenError)
+        def handle_revoked_token_error(e):
+            return self._revoked_token_callback(e.jwt_header, e.jwt_data)
+
+        @app.errorhandler(UserClaimsVerificationError)
+        def handle_failed_token_verification(e):
+            return self._token_verification_failed_callback(e.jwt_header, e.jwt_data)
+
+        @app.errorhandler(UserLookupError)
+        def handler_user_lookup_error(e):
+            return self._user_lookup_error_callback(e.jwt_header, e.jwt_data)
+
+        @app.errorhandler(WrongTokenError)
+        def handle_wrong_token_error(e):
+            return self._invalid_token_callback(str(e))
+
+    @staticmethod
+    def _set_default_configuration_options(app: Flask) -> None:
+        app.config.setdefault(
+            "JWT_ACCESS_TOKEN_EXPIRES", datetime.timedelta(minutes=15)
+        )
+        app.config.setdefault("JWT_ACCESS_COOKIE_NAME", "access_token_cookie")
+        app.config.setdefault("JWT_ACCESS_COOKIE_PATH", "/")
+        app.config.setdefault("JWT_ACCESS_CSRF_COOKIE_NAME", "csrf_access_token")
+        app.config.setdefault("JWT_ACCESS_CSRF_COOKIE_PATH", "/")
+        app.config.setdefault("JWT_ACCESS_CSRF_FIELD_NAME", "csrf_token")
+        app.config.setdefault("JWT_ACCESS_CSRF_HEADER_NAME", "X-CSRF-TOKEN")
+        app.config.setdefault("JWT_ALGORITHM", "HS256")
+        app.config.setdefault("JWT_COOKIE_CSRF_PROTECT", True)
+        app.config.setdefault("JWT_COOKIE_DOMAIN", None)
+        app.config.setdefault("JWT_COOKIE_SAMESITE", None)
+        app.config.setdefault("JWT_COOKIE_SECURE", False)
+        app.config.setdefault("JWT_CSRF_CHECK_FORM", False)
+        app.config.setdefault("JWT_CSRF_IN_COOKIES", True)
+        app.config.setdefault("JWT_CSRF_METHODS", ["POST", "PUT", "PATCH", "DELETE"])
+        app.config.setdefault("JWT_DECODE_ALGORITHMS", None)
+        app.config.setdefault("JWT_DECODE_AUDIENCE", None)
+        app.config.setdefault("JWT_DECODE_ISSUER", None)
+        app.config.setdefault("JWT_DECODE_LEEWAY", 0)
+        app.config.setdefault("JWT_ENCODE_AUDIENCE", None)
+        app.config.setdefault("JWT_ENCODE_ISSUER", None)
+        app.config.setdefault("JWT_ERROR_MESSAGE_KEY", "msg")
+        app.config.setdefault("JWT_HEADER_NAME", "Authorization")
+        app.config.setdefault("JWT_HEADER_TYPE", "Bearer")
+        app.config.setdefault("JWT_IDENTITY_CLAIM", "sub")
+        app.config.setdefault("JWT_JSON_KEY", "access_token")
+        app.config.setdefault("JWT_PRIVATE_KEY", None)
+        app.config.setdefault("JWT_PUBLIC_KEY", None)
+        app.config.setdefault("JWT_QUERY_STRING_NAME", "jwt")
+        app.config.setdefault("JWT_QUERY_STRING_VALUE_PREFIX", "")
+        app.config.setdefault("JWT_REFRESH_COOKIE_NAME", "refresh_token_cookie")
+        app.config.setdefault("JWT_REFRESH_COOKIE_PATH", "/")
+        app.config.setdefault("JWT_REFRESH_CSRF_COOKIE_NAME", "csrf_refresh_token")
+        app.config.setdefault("JWT_REFRESH_CSRF_COOKIE_PATH", "/")
+        app.config.setdefault("JWT_REFRESH_CSRF_FIELD_NAME", "csrf_token")
+        app.config.setdefault("JWT_REFRESH_CSRF_HEADER_NAME", "X-CSRF-TOKEN")
+        app.config.setdefault("JWT_REFRESH_JSON_KEY", "refresh_token")
+        app.config.setdefault("JWT_REFRESH_TOKEN_EXPIRES", datetime.timedelta(days=30))
+        app.config.setdefault("JWT_SECRET_KEY", None)
+        app.config.setdefault("JWT_SESSION_COOKIE", True)
+        app.config.setdefault("JWT_TOKEN_LOCATION", ("headers",))
+        app.config.setdefault("JWT_ENCODE_NBF", True)
+
+    def additional_claims_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function used to add additional claims
+        when creating a JWT. The claims returned by this function will be merged
+        with any claims passed in via the ``additional_claims`` argument to
+        :func:`~flask_jwt_extended.create_access_token` or
+        :func:`~flask_jwt_extended.create_refresh_token`.
+
+        The decorated function must take **one** argument.
+
+        The argument is the identity that was used when creating a JWT.
+
+        The decorated function must return a dictionary of claims to add to the JWT.
+        """
+        self._user_claims_callback = callback
+        return callback
+
+    def additional_headers_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function used to add additional headers
+        when creating a JWT. The headers returned by this function will be merged
+        with any headers passed in via the ``additional_headers`` argument to
+        :func:`~flask_jwt_extended.create_access_token` or
+        :func:`~flask_jwt_extended.create_refresh_token`.
+
+        The decorated function must take **one** argument.
+
+        The argument is the identity that was used when creating a JWT.
+
+        The decorated function must return a dictionary of headers to add to the JWT.
+        """
+        self._jwt_additional_header_callback = callback
+        return callback
+
+    def decode_key_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function for dynamically setting the JWT
+        decode key based on the **UNVERIFIED** contents of the token. Think
+        carefully before using this functionality, in most cases you probably
+        don't need it.
+
+        The decorated function must take **two** arguments.
+
+        The first argument is a dictionary containing the header data of the
+        unverified JWT.
+
+        The second argument is a dictionary containing the payload data of the
+        unverified JWT.
+
+        The decorated function must return a *string* that is used to decode and
+        verify the token.
+        """
+        self._decode_key_callback = callback
+        return callback
+
+    def encode_key_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function for dynamically setting the JWT
+        encode key based on the tokens identity. Think carefully before using this
+        functionality, in most cases you probably don't need it.
+
+        The decorated function must take **one** argument.
+
+        The argument is the identity used to create this JWT.
+
+        The decorated function must return a *string* which is the secrete key used to
+        encode the JWT.
+        """
+        self._encode_key_callback = callback
+        return callback
+
+    def expired_token_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function for returning a custom
+        response when an expired JWT is encountered.
+
+        The decorated function must take **two** arguments.
+
+        The first argument is a dictionary containing the header data of the JWT.
+
+        The second argument is a dictionary containing the payload data of the JWT.
+
+        The decorated function must return a Flask Response.
+        """
+        self._expired_token_callback = callback
+        return callback
+
+    def invalid_token_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function for returning a custom
+        response when an invalid JWT is encountered.
+
+        This decorator sets the callback function that will be used if an
+        invalid JWT attempts to access a protected endpoint.
+
+        The decorated function must take **one** argument.
+
+        The argument is a string which contains the reason why a token is invalid.
+
+        The decorated function must return a Flask Response.
+        """
+        self._invalid_token_callback = callback
+        return callback
+
+    def needs_fresh_token_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function for returning a custom
+        response when a valid and non-fresh token is used on an endpoint
+        that is marked as ``fresh=True``.
+
+        The decorated function must take **two** arguments.
+
+        The first argument is a dictionary containing the header data of the JWT.
+
+        The second argument is a dictionary containing the payload data of the JWT.
+
+        The decorated function must return a Flask Response.
+        """
+        self._needs_fresh_token_callback = callback
+        return callback
+
+    def revoked_token_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function for returning a custom
+        response when a revoked token is encountered.
+
+        The decorated function must take **two** arguments.
+
+        The first argument is a dictionary containing the header data of the JWT.
+
+        The second argument is a dictionary containing the payload data of the JWT.
+
+        The decorated function must return a Flask Response.
+        """
+        self._revoked_token_callback = callback
+        return callback
+
+    def token_in_blocklist_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function used to check if a JWT has
+        been revoked.
+
+        The decorated function must take **two** arguments.
+
+        The first argument is a dictionary containing the header data of the JWT.
+
+        The second argument is a dictionary containing the payload data of the JWT.
+
+        The decorated function must be return ``True`` if the token has been
+        revoked, ``False`` otherwise.
+        """
+        self._token_in_blocklist_callback = callback
+        return callback
+
+    def token_verification_failed_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function used to return a custom
+        response when the claims verification check fails.
+
+        The decorated function must take **two** arguments.
+
+        The first argument is a dictionary containing the header data of the JWT.
+
+        The second argument is a dictionary containing the payload data of the JWT.
+
+        The decorated function must return a Flask Response.
+        """
+        self._token_verification_failed_callback = callback
+        return callback
+
+    def token_verification_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function used for custom verification
+        of a valid JWT.
+
+        The decorated function must take **two** arguments.
+
+        The first argument is a dictionary containing the header data of the JWT.
+
+        The second argument is a dictionary containing the payload data of the JWT.
+
+        The decorated function must return ``True`` if the token is valid, or
+        ``False`` otherwise.
+        """
+        self._token_verification_callback = callback
+        return callback
+
+    def unauthorized_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function used to return a custom
+        response when no JWT is present.
+
+        The decorated function must take **one** argument.
+
+        The argument is a string that explains why the JWT could not be found.
+
+        The decorated function must return a Flask Response.
+        """
+        self._unauthorized_callback = callback
+        return callback
+
+    def user_identity_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function used to convert an identity to
+        a JSON serializable format when creating JWTs. This is useful for
+        using objects (such as SQLAlchemy instances) as the identity when
+        creating your tokens.
+
+        The decorated function must take **one** argument.
+
+        The argument is the identity that was used when creating a JWT.
+
+        The decorated function must return JSON serializable data.
+        """
+        self._user_identity_callback = callback
+        return callback
+
+    def user_lookup_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function used to convert a JWT into
+        a python object that can be used in a protected endpoint. This is useful
+        for automatically loading a SQLAlchemy instance based on the contents
+        of the JWT.
+
+        The object returned from this function can be accessed via
+        :attr:`~flask_jwt_extended.current_user` or
+        :meth:`~flask_jwt_extended.get_current_user`
+
+        The decorated function must take **two** arguments.
+
+        The first argument is a dictionary containing the header data of the JWT.
+
+        The second argument is a dictionary containing the payload data of the JWT.
+
+        The decorated function can return any python object, which can then be
+        accessed in a protected endpoint. If an object cannot be loaded, for
+        example if a user has been deleted from your database, ``None`` must be
+        returned to indicate that an error occurred loading the user.
+        """
+        self._user_lookup_callback = callback
+        return callback
+
+    def user_lookup_error_loader(self, callback: Callable) -> Callable:
+        """
+        This decorator sets the callback function used to return a custom
+        response when loading a user via
+        :meth:`~flask_jwt_extended.JWTManager.user_lookup_loader` fails.
+
+        The decorated function must take **two** arguments.
+
+        The first argument is a dictionary containing the header data of the JWT.
+
+        The second argument is a dictionary containing the payload data of the JWT.
+
+        The decorated function must return a Flask Response.
+        """
+        self._user_lookup_error_callback = callback
+        return callback
+
+    def _encode_jwt_from_config(
+        self,
+        identity: Any,
+        token_type: str,
+        claims=None,
+        fresh: bool = False,
+        expires_delta: ExpiresDelta = None,
+        headers=None,
+    ) -> str:
+        header_overrides = self._jwt_additional_header_callback(identity)
+        if headers is not None:
+            header_overrides.update(headers)
+
+        claim_overrides = self._user_claims_callback(identity)
+        if claims is not None:
+            claim_overrides.update(claims)
+
+        if expires_delta is None:
+            if token_type == "access":
+                expires_delta = config.access_expires
+            else:
+                expires_delta = config.refresh_expires
+
+        return _encode_jwt(
+            algorithm=config.algorithm,
+            audience=config.encode_audience,
+            claim_overrides=claim_overrides,
+            csrf=config.csrf_protect,
+            expires_delta=expires_delta,
+            fresh=fresh,
+            header_overrides=header_overrides,
+            identity=self._user_identity_callback(identity),
+            identity_claim_key=config.identity_claim_key,
+            issuer=config.encode_issuer,
+            json_encoder=config.json_encoder,
+            secret=self._encode_key_callback(identity),
+            token_type=token_type,
+            nbf=config.encode_nbf,
+        )
+
+    def _decode_jwt_from_config(
+        self, encoded_token: str, csrf_value=None, allow_expired: bool = False
+    ) -> dict:
+        unverified_claims = jwt.decode(
+            encoded_token,
+            algorithms=config.decode_algorithms,
+            options={"verify_signature": False},
+        )
+        unverified_headers = jwt.get_unverified_header(encoded_token)
+        secret = self._decode_key_callback(unverified_headers, unverified_claims)
+
+        kwargs = {
+            "algorithms": config.decode_algorithms,
+            "audience": config.decode_audience,
+            "csrf_value": csrf_value,
+            "encoded_token": encoded_token,
+            "identity_claim_key": config.identity_claim_key,
+            "issuer": config.decode_issuer,
+            "leeway": config.leeway,
+            "secret": secret,
+            "verify_aud": config.decode_audience is not None,
+        }
+
+        try:
+            return _decode_jwt(**kwargs, allow_expired=allow_expired)
+        except ExpiredSignatureError as e:
+            # TODO: If we ever do another breaking change, don't raise this pyjwt
+            #       error directly, instead raise a custom error of ours from this
+            #       error.
+            e.jwt_header = unverified_headers  # type: ignore
+            e.jwt_data = _decode_jwt(**kwargs, allow_expired=True)  # type: ignore
+            raise

+ 0 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/py.typed


+ 123 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/tokens.py

@@ -0,0 +1,123 @@
+import uuid
+from datetime import datetime
+from datetime import timedelta
+from datetime import timezone
+from hmac import compare_digest
+from typing import Any
+from typing import Iterable
+from typing import List
+from typing import Type
+from typing import Union
+
+import jwt
+from flask.json import JSONEncoder
+
+from flask_jwt_extended.exceptions import CSRFError
+from flask_jwt_extended.exceptions import JWTDecodeError
+from flask_jwt_extended.typing import ExpiresDelta
+
+
+def _encode_jwt(
+    algorithm: str,
+    audience: Union[str, Iterable[str]],
+    claim_overrides: dict,
+    csrf: bool,
+    expires_delta: ExpiresDelta,
+    fresh: bool,
+    header_overrides: dict,
+    identity: Any,
+    identity_claim_key: str,
+    issuer: str,
+    json_encoder: Type[JSONEncoder],
+    secret: str,
+    token_type: str,
+    nbf: bool,
+) -> str:
+    now = datetime.now(timezone.utc)
+
+    if isinstance(fresh, timedelta):
+        fresh = datetime.timestamp(now + fresh)
+
+    token_data = {
+        "fresh": fresh,
+        "iat": now,
+        "jti": str(uuid.uuid4()),
+        "type": token_type,
+        identity_claim_key: identity,
+    }
+
+    if nbf:
+        token_data["nbf"] = now
+
+    if csrf:
+        token_data["csrf"] = str(uuid.uuid4())
+
+    if audience:
+        token_data["aud"] = audience
+
+    if issuer:
+        token_data["iss"] = issuer
+
+    if expires_delta:
+        token_data["exp"] = now + expires_delta
+
+    if claim_overrides:
+        token_data.update(claim_overrides)
+
+    return jwt.encode(
+        token_data,
+        secret,
+        algorithm,
+        json_encoder=json_encoder,  # type: ignore
+        headers=header_overrides,
+    )
+
+
+def _decode_jwt(
+    algorithms: List,
+    allow_expired: bool,
+    audience: Union[str, Iterable[str]],
+    csrf_value: str,
+    encoded_token: str,
+    identity_claim_key: str,
+    issuer: str,
+    leeway: int,
+    secret: str,
+    verify_aud: bool,
+) -> dict:
+    options = {"verify_aud": verify_aud}
+    if allow_expired:
+        options["verify_exp"] = False
+
+    # This call verifies the ext, iat, and nbf claims
+    # This optionally verifies the exp and aud claims if enabled
+    decoded_token = jwt.decode(
+        encoded_token,
+        secret,
+        algorithms=algorithms,
+        audience=audience,
+        issuer=issuer,
+        leeway=leeway,
+        options=options,
+    )
+
+    # Make sure that any custom claims we expect in the token are present
+    if identity_claim_key not in decoded_token:
+        raise JWTDecodeError("Missing claim: {}".format(identity_claim_key))
+
+    if "type" not in decoded_token:
+        decoded_token["type"] = "access"
+
+    if "fresh" not in decoded_token:
+        decoded_token["fresh"] = False
+
+    if "jti" not in decoded_token:
+        decoded_token["jti"] = None
+
+    if csrf_value:
+        if "csrf" not in decoded_token:
+            raise JWTDecodeError("Missing claim: csrf")
+        if not compare_digest(decoded_token["csrf"], csrf_value):
+            raise CSRFError("CSRF double submit tokens do not match")
+
+    return decoded_token

+ 10 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/typing.py

@@ -0,0 +1,10 @@
+import sys
+from typing import Any
+from typing import Union
+
+if sys.version_info >= (3, 8):
+    from typing import Literal  # pragma: no cover
+else:
+    from typing_extensions import Literal  # pragma: no cover
+
+ExpiresDelta = Union[Literal[False], Any]

+ 458 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/utils.py

@@ -0,0 +1,458 @@
+import datetime
+from typing import Any
+from typing import Optional
+
+import jwt
+from flask import _request_ctx_stack
+from flask import Response
+from werkzeug.local import LocalProxy
+
+from flask_jwt_extended.config import config
+from flask_jwt_extended.internal_utils import get_jwt_manager
+
+# Proxy to access the current user
+current_user = LocalProxy(lambda: get_current_user())
+
+
+def get_jwt() -> dict:
+    """
+    In a protected endpoint, this will return the python dictionary which has
+    the payload of the JWT that is accessing the endpoint. If no JWT is present
+    due to ``jwt_required(optional=True)``, an empty dictionary is returned.
+
+    :return:
+        The payload (claims) of the JWT in the current request
+    """
+    decoded_jwt = getattr(_request_ctx_stack.top, "jwt", None)
+    if decoded_jwt is None:
+        raise RuntimeError(
+            "You must call `@jwt_required()` or `verify_jwt_in_request()` "
+            "before using this method"
+        )
+    return decoded_jwt
+
+
+def get_jwt_header() -> dict:
+    """
+    In a protected endpoint, this will return the python dictionary which has
+    the header of the JWT that is accessing the endpoint. If no JWT is present
+    due to ``jwt_required(optional=True)``, an empty dictionary is returned.
+
+    :return:
+        The headers of the JWT in the current request
+    """
+    decoded_header = getattr(_request_ctx_stack.top, "jwt_header", None)
+    if decoded_header is None:
+        raise RuntimeError(
+            "You must call `@jwt_required()` or `verify_jwt_in_request()` "
+            "before using this method"
+        )
+    return decoded_header
+
+
+def get_jwt_identity() -> Any:
+    """
+    In a protected endpoint, this will return the identity of the JWT that is
+    accessing the endpoint. If no JWT is present due to
+    ``jwt_required(optional=True)``, ``None`` is returned.
+
+    :return:
+        The identity of the JWT in the current request
+    """
+    return get_jwt().get(config.identity_claim_key, None)
+
+
+def get_jwt_request_location() -> Optional[str]:
+    """
+    In a protected endpoint, this will return the "location" at which the JWT
+    that is accessing the endpoint was found--e.g., "cookies", "query-string",
+    "headers", or "json". If no JWT is present due to ``jwt_required(optional=True)``,
+    None is returned.
+
+    :return:
+        The location of the JWT in the current request; e.g., "cookies",
+        "query-string", "headers", or "json"
+    """
+    return getattr(_request_ctx_stack.top, "jwt_location", None)
+
+
+def get_current_user() -> Any:
+    """
+    In a protected endpoint, this will return the user object for the JWT that
+    is accessing the endpoint.
+
+    This is only usable if :meth:`~flask_jwt_extended.JWTManager.user_lookup_loader`
+    is configured. If the user loader callback is not being used, this will
+    raise an error.
+
+    If no JWT is present due to ``jwt_required(optional=True)``, ``None`` is returned.
+
+    :return:
+        The current user object for the JWT in the current request
+    """
+    get_jwt()  # Raise an error if not in a decorated context
+    jwt_user_dict = getattr(_request_ctx_stack.top, "jwt_user", None)
+    if jwt_user_dict is None:
+        raise RuntimeError(
+            "You must provide a `@jwt.user_lookup_loader` callback to use "
+            "this method"
+        )
+    return jwt_user_dict["loaded_user"]
+
+
+def decode_token(
+    encoded_token: str, csrf_value: str = None, allow_expired: bool = False
+) -> dict:
+    """
+    Returns the decoded token (python dict) from an encoded JWT. This does all
+    the checks to ensure that the decoded token is valid before returning it.
+
+    This will not fire the user loader callbacks, save the token for access
+    in protected endpoints, checked if a token is revoked, etc. This is puerly
+    used to ensure that a JWT is valid.
+
+    :param encoded_token:
+        The encoded JWT to decode.
+
+    :param csrf_value:
+        Expected CSRF double submit value (optional).
+
+    :param allow_expired:
+        If ``True``, do not raise an error if the JWT is expired.  Defaults to ``False``
+
+    :return:
+        Dictionary containing the payload of the JWT decoded JWT.
+    """
+    jwt_manager = get_jwt_manager()
+    return jwt_manager._decode_jwt_from_config(encoded_token, csrf_value, allow_expired)
+
+
+def create_access_token(
+    identity: Any,
+    fresh: bool = False,
+    expires_delta: datetime.timedelta = None,
+    additional_claims=None,
+    additional_headers=None,
+):
+    """
+    Create a new access token.
+
+    :param identity:
+        The identity of this token. It can be any data that is json serializable.
+        You can use :meth:`~flask_jwt_extended.JWTManager.user_identity_loader`
+        to define a callback function to convert any object passed in into a json
+        serializable format.
+
+    :param fresh:
+        If this token should be marked as fresh, and can thus access endpoints
+        protected with ``@jwt_required(fresh=True)``. Defaults to ``False``.
+
+        This value can also be a ``datetime.timedelta``, which indicate
+        how long this token will be considered fresh.
+
+    :param expires_delta:
+        A ``datetime.timedelta`` for how long this token should last before it
+        expires. Set to False to disable expiration. If this is None, it will use
+        the ``JWT_ACCESS_TOKEN_EXPIRES`` config value (see :ref:`Configuration Options`)
+
+    :param additional_claims:
+        Optional. A hash of claims to include in the access token.  These claims are
+        merged into the default claims (exp, iat, etc) and claims returned from the
+        :meth:`~flask_jwt_extended.JWTManager.additional_claims_loader` callback.
+        On conflict, these claims take presidence.
+
+    :param headers:
+        Optional. A hash of headers to include in the access token. These headers
+        are merged into the default headers (alg, typ) and headers returned from
+        the :meth:`~flask_jwt_extended.JWTManager.additional_headers_loader`
+        callback. On conflict, these headers take presidence.
+
+    :return:
+        An encoded access token
+    """
+    jwt_manager = get_jwt_manager()
+    return jwt_manager._encode_jwt_from_config(
+        claims=additional_claims,
+        expires_delta=expires_delta,
+        fresh=fresh,
+        headers=additional_headers,
+        identity=identity,
+        token_type="access",
+    )
+
+
+def create_refresh_token(
+    identity: Any,
+    expires_delta: datetime.timedelta = None,
+    additional_claims=None,
+    additional_headers=None,
+):
+    """
+    Create a new refresh token.
+
+    :param identity:
+        The identity of this token. It can be any data that is json serializable.
+        You can use :meth:`~flask_jwt_extended.JWTManager.user_identity_loader`
+        to define a callback function to convert any object passed in into a json
+        serializable format.
+
+    :param expires_delta:
+        A ``datetime.timedelta`` for how long this token should last before it expires.
+        Set to False to disable expiration. If this is None, it will use the
+        ``JWT_REFRESH_TOKEN_EXPIRES`` config value (see :ref:`Configuration Options`)
+
+    :param additional_claims:
+        Optional. A hash of claims to include in the refresh token. These claims are
+        merged into the default claims (exp, iat, etc) and claims returned from the
+        :meth:`~flask_jwt_extended.JWTManager.additional_claims_loader` callback.
+        On conflict, these claims take presidence.
+
+    :param headers:
+        Optional. A hash of headers to include in the refresh token. These headers
+        are merged into the default headers (alg, typ) and headers returned from the
+        :meth:`~flask_jwt_extended.JWTManager.additional_headers_loader` callback.
+        On conflict, these headers take presidence.
+
+    :return:
+        An encoded refresh token
+    """
+    jwt_manager = get_jwt_manager()
+    return jwt_manager._encode_jwt_from_config(
+        claims=additional_claims,
+        expires_delta=expires_delta,
+        fresh=False,
+        headers=additional_headers,
+        identity=identity,
+        token_type="refresh",
+    )
+
+
+def get_unverified_jwt_headers(encoded_token: str) -> dict:
+    """
+    Returns the Headers of an encoded JWT without verifying the signature of the JWT.
+
+    :param encoded_token:
+        The encoded JWT to get the Header from.
+
+    :return:
+        JWT header parameters as python dict()
+    """
+    return jwt.get_unverified_header(encoded_token)
+
+
+def get_jti(encoded_token: str) -> Optional[str]:
+    """
+    Returns the JTI (unique identifier) of an encoded JWT
+
+    :param encoded_token:
+        The encoded JWT to get the JTI from.
+
+    :return:
+        The JTI (unique identifier) of a JWT, if it is present.
+    """
+    return decode_token(encoded_token).get("jti")
+
+
+def get_csrf_token(encoded_token: str) -> str:
+    """
+    Returns the CSRF double submit token from an encoded JWT.
+
+    :param encoded_token:
+        The encoded JWT
+
+    :return:
+        The CSRF double submit token (string)
+    """
+    token = decode_token(encoded_token)
+    return token["csrf"]
+
+
+def set_access_cookies(
+    response: Response, encoded_access_token: str, max_age=None, domain=None
+) -> None:
+    """
+    Modifiy a Flask Response to set a cookie containing the access JWT.
+    Also sets the corresponding CSRF cookies if ``JWT_CSRF_IN_COOKIES`` is ``True``
+    (see :ref:`Configuration Options`)
+
+    :param response:
+        A Flask Response object.
+
+    :param encoded_access_token:
+        The encoded access token to set in the cookies.
+
+    :param max_age:
+        The max age of the cookie. If this is None, it will use the
+        ``JWT_SESSION_COOKIE`` option (see :ref:`Configuration Options`). Otherwise,
+        it will use this as the cookies ``max-age`` and the JWT_SESSION_COOKIE option
+        will be ignored. Values should be the number of seconds (as an integer).
+
+    :param domain:
+        The domain of the cookie. If this is None, it will use the
+        ``JWT_COOKIE_DOMAIN`` option (see :ref:`Configuration Options`). Otherwise,
+        it will use this as the cookies ``domain`` and the JWT_COOKIE_DOMAIN option
+        will be ignored.
+    """
+    response.set_cookie(
+        config.access_cookie_name,
+        value=encoded_access_token,
+        max_age=max_age or config.cookie_max_age,
+        secure=config.cookie_secure,
+        httponly=True,
+        domain=domain or config.cookie_domain,
+        path=config.access_cookie_path,
+        samesite=config.cookie_samesite,
+    )
+
+    if config.csrf_protect and config.csrf_in_cookies:
+        response.set_cookie(
+            config.access_csrf_cookie_name,
+            value=get_csrf_token(encoded_access_token),
+            max_age=max_age or config.cookie_max_age,
+            secure=config.cookie_secure,
+            httponly=False,
+            domain=domain or config.cookie_domain,
+            path=config.access_csrf_cookie_path,
+            samesite=config.cookie_samesite,
+        )
+
+
+def set_refresh_cookies(
+    response: Response,
+    encoded_refresh_token: str,
+    max_age: int = None,
+    domain: str = None,
+) -> None:
+    """
+    Modifiy a Flask Response to set a cookie containing the refresh JWT.
+    Also sets the corresponding CSRF cookies if ``JWT_CSRF_IN_COOKIES`` is ``True``
+    (see :ref:`Configuration Options`)
+
+    :param response:
+        A Flask Response object.
+
+    :param encoded_refresh_token:
+        The encoded refresh token to set in the cookies.
+
+    :param max_age:
+        The max age of the cookie. If this is None, it will use the
+        ``JWT_SESSION_COOKIE`` option (see :ref:`Configuration Options`). Otherwise,
+        it will use this as the cookies ``max-age`` and the JWT_SESSION_COOKIE option
+        will be ignored. Values should be the number of seconds (as an integer).
+
+    :param domain:
+        The domain of the cookie. If this is None, it will use the
+        ``JWT_COOKIE_DOMAIN`` option (see :ref:`Configuration Options`). Otherwise,
+        it will use this as the cookies ``domain`` and the JWT_COOKIE_DOMAIN option
+        will be ignored.
+    """
+    response.set_cookie(
+        config.refresh_cookie_name,
+        value=encoded_refresh_token,
+        max_age=max_age or config.cookie_max_age,
+        secure=config.cookie_secure,
+        httponly=True,
+        domain=domain or config.cookie_domain,
+        path=config.refresh_cookie_path,
+        samesite=config.cookie_samesite,
+    )
+
+    if config.csrf_protect and config.csrf_in_cookies:
+        response.set_cookie(
+            config.refresh_csrf_cookie_name,
+            value=get_csrf_token(encoded_refresh_token),
+            max_age=max_age or config.cookie_max_age,
+            secure=config.cookie_secure,
+            httponly=False,
+            domain=domain or config.cookie_domain,
+            path=config.refresh_csrf_cookie_path,
+            samesite=config.cookie_samesite,
+        )
+
+
+def unset_jwt_cookies(response: Response, domain: str = None) -> None:
+    """
+    Modifiy a Flask Response to delete the cookies containing access or refresh
+    JWTs.  Also deletes the corresponding CSRF cookies if applicable.
+
+    :param response:
+        A Flask Response object
+    """
+    unset_access_cookies(response, domain)
+    unset_refresh_cookies(response, domain)
+
+
+def unset_access_cookies(response: Response, domain: str = None) -> None:
+    """
+    Modifiy a Flask Response to delete the cookie containing an access JWT.
+    Also deletes the corresponding CSRF cookie if applicable.
+
+    :param response:
+        A Flask Response object
+
+    :param domain:
+        The domain of the cookie. If this is None, it will use the
+        ``JWT_COOKIE_DOMAIN`` option (see :ref:`Configuration Options`). Otherwise,
+        it will use this as the cookies ``domain`` and the JWT_COOKIE_DOMAIN option
+        will be ignored.
+    """
+    response.set_cookie(
+        config.access_cookie_name,
+        value="",
+        expires=0,
+        secure=config.cookie_secure,
+        httponly=True,
+        domain=domain or config.cookie_domain,
+        path=config.access_cookie_path,
+        samesite=config.cookie_samesite,
+    )
+
+    if config.csrf_protect and config.csrf_in_cookies:
+        response.set_cookie(
+            config.access_csrf_cookie_name,
+            value="",
+            expires=0,
+            secure=config.cookie_secure,
+            httponly=False,
+            domain=domain or config.cookie_domain,
+            path=config.access_csrf_cookie_path,
+            samesite=config.cookie_samesite,
+        )
+
+
+def unset_refresh_cookies(response: Response, domain: str = None) -> None:
+    """
+    Modifiy a Flask Response to delete the cookie containing a refresh JWT.
+    Also deletes the corresponding CSRF cookie if applicable.
+
+    :param response:
+        A Flask Response object
+
+    :param domain:
+        The domain of the cookie. If this is None, it will use the
+        ``JWT_COOKIE_DOMAIN`` option (see :ref:`Configuration Options`). Otherwise,
+        it will use this as the cookies ``domain`` and the JWT_COOKIE_DOMAIN option
+        will be ignored.
+    """
+    response.set_cookie(
+        config.refresh_cookie_name,
+        value="",
+        expires=0,
+        secure=config.cookie_secure,
+        httponly=True,
+        domain=domain or config.cookie_domain,
+        path=config.refresh_cookie_path,
+        samesite=config.cookie_samesite,
+    )
+
+    if config.csrf_protect and config.csrf_in_cookies:
+        response.set_cookie(
+            config.refresh_csrf_cookie_name,
+            value="",
+            expires=0,
+            secure=config.cookie_secure,
+            httponly=False,
+            domain=domain or config.cookie_domain,
+            path=config.refresh_csrf_cookie_path,
+            samesite=config.cookie_samesite,
+        )

+ 352 - 0
rest_venv/Lib/site-packages/flask_jwt_extended/view_decorators.py

@@ -0,0 +1,352 @@
+from datetime import datetime
+from datetime import timezone
+from functools import wraps
+from re import split
+from typing import Any
+from typing import Optional
+from typing import Sequence
+from typing import Tuple
+from typing import Union
+
+from flask import _request_ctx_stack
+from flask import current_app
+from flask import request
+from werkzeug.exceptions import BadRequest
+
+from flask_jwt_extended.config import config
+from flask_jwt_extended.exceptions import CSRFError
+from flask_jwt_extended.exceptions import FreshTokenRequired
+from flask_jwt_extended.exceptions import InvalidHeaderError
+from flask_jwt_extended.exceptions import InvalidQueryParamError
+from flask_jwt_extended.exceptions import NoAuthorizationError
+from flask_jwt_extended.exceptions import UserLookupError
+from flask_jwt_extended.internal_utils import custom_verification_for_token
+from flask_jwt_extended.internal_utils import has_user_lookup
+from flask_jwt_extended.internal_utils import user_lookup
+from flask_jwt_extended.internal_utils import verify_token_not_blocklisted
+from flask_jwt_extended.internal_utils import verify_token_type
+from flask_jwt_extended.utils import decode_token
+from flask_jwt_extended.utils import get_unverified_jwt_headers
+
+LocationType = Union[str, Sequence, None]
+
+
+def _verify_token_is_fresh(jwt_header: dict, jwt_data: dict) -> None:
+    fresh = jwt_data["fresh"]
+    if isinstance(fresh, bool):
+        if not fresh:
+            raise FreshTokenRequired("Fresh token required", jwt_header, jwt_data)
+    else:
+        now = datetime.timestamp(datetime.now(timezone.utc))
+        if fresh < now:
+            raise FreshTokenRequired("Fresh token required", jwt_header, jwt_data)
+
+
+def verify_jwt_in_request(
+    optional: bool = False,
+    fresh: bool = False,
+    refresh: bool = False,
+    locations: LocationType = None,
+    verify_type: bool = False,
+) -> Optional[Tuple[dict, dict]]:
+    """
+    Verify that a valid JWT is present in the request, unless ``optional=True`` in
+    which case no JWT is also considered valid.
+
+    :param optional:
+        If ``True``, do not raise an error if no JWT is present in the request.
+        Defaults to ``False``.
+
+    :param fresh:
+        If ``True``, require a JWT marked as ``fresh`` in order to be verified.
+        Defaults to ``False``.
+
+    :param refresh:
+        If ``True``, requires a refresh JWT to access this endpoint. If ``False``,
+        requires an access JWT to access this endpoint. Defaults to ``False``
+
+    :param locations:
+        A location or list of locations to look for the JWT in this request, for
+        example ``'headers'`` or ``['headers', 'cookies']``. Defaults to ``None``
+        which indicates that JWTs will be looked for in the locations defined by the
+        ``JWT_TOKEN_LOCATION`` configuration option.
+
+    :param verify_type:
+        If ``True``, the token type (access or refresh) will be checked according
+        to the ``refresh`` argument. If ``False``, type will not be checked and both
+        access and refresh tokens will be accepted.
+
+    :return:
+        A tuple containing the jwt_header and the jwt_data if a valid JWT is
+        present in the request. If ``optional=True`` and no JWT is in the request,
+        ``None`` will be returned instead. Raise an exception if an invalid JWT
+        is in the request.
+    """
+    if request.method in config.exempt_methods:
+        return None
+
+    try:
+        jwt_data, jwt_header, jwt_location = _decode_jwt_from_request(
+            locations, fresh, refresh=refresh, verify_type=verify_type
+        )
+
+    except NoAuthorizationError:
+        if not optional:
+            raise
+        _request_ctx_stack.top.jwt = {}
+        _request_ctx_stack.top.jwt_header = {}
+        _request_ctx_stack.top.jwt_user = {"loaded_user": None}
+        _request_ctx_stack.top.jwt_location = None
+        return None
+
+    # Save these at the very end so that they are only saved in the requet
+    # context if the token is valid and all callbacks succeed
+    _request_ctx_stack.top.jwt_user = _load_user(jwt_header, jwt_data)
+    _request_ctx_stack.top.jwt_header = jwt_header
+    _request_ctx_stack.top.jwt = jwt_data
+    _request_ctx_stack.top.jwt_location = jwt_location
+
+    return jwt_header, jwt_data
+
+
+def jwt_required(
+    optional: bool = False,
+    fresh: bool = False,
+    refresh: bool = False,
+    locations: LocationType = None,
+    verify_type: bool = True,
+) -> Any:
+    """
+    A decorator to protect a Flask endpoint with JSON Web Tokens.
+
+    Any route decorated with this will require a valid JWT to be present in the
+    request (unless optional=True, in which case no JWT is also valid) before the
+    endpoint can be called.
+
+    :param optional:
+        If ``True``, allow the decorated endpoint to be accessed if no JWT is present in
+        the request. Defaults to ``False``.
+
+    :param fresh:
+        If ``True``, require a JWT marked with ``fresh`` to be able to access this
+        endpoint. Defaults to ``False``.
+
+    :param refresh:
+        If ``True``, requires a refresh JWT to access this endpoint. If ``False``,
+        requires an access JWT to access this endpoint. Defaults to ``False``.
+
+    :param locations:
+        A location or list of locations to look for the JWT in this request, for
+        example ``'headers'`` or ``['headers', 'cookies']``. Defaults to ``None``
+        which indicates that JWTs will be looked for in the locations defined by the
+        ``JWT_TOKEN_LOCATION`` configuration option.
+
+    :param verify_type:
+        If ``True``, the token type (access or refresh) will be checked according
+        to the ``refresh`` argument. If ``False``, type will not be checked and both
+        access and refresh tokens will be accepted.
+    """
+
+    def wrapper(fn):
+        @wraps(fn)
+        def decorator(*args, **kwargs):
+            verify_jwt_in_request(optional, fresh, refresh, locations, verify_type)
+            return current_app.ensure_sync(fn)(*args, **kwargs)
+
+        return decorator
+
+    return wrapper
+
+
+def _load_user(jwt_header: dict, jwt_data: dict) -> Optional[dict]:
+    if not has_user_lookup():
+        return None
+
+    identity = jwt_data[config.identity_claim_key]
+    user = user_lookup(jwt_header, jwt_data)
+    if user is None:
+        error_msg = "user_lookup returned None for {}".format(identity)
+        raise UserLookupError(error_msg, jwt_header, jwt_data)
+    return {"loaded_user": user}
+
+
+def _decode_jwt_from_headers() -> Tuple[str, None]:
+    header_name = config.header_name
+    header_type = config.header_type
+
+    # Verify we have the auth header
+    auth_header = request.headers.get(header_name, "").strip().strip(",")
+    if not auth_header:
+        raise NoAuthorizationError(f"Missing {header_name} Header")
+
+    # Make sure the header is in a valid format that we are expecting, ie
+    # <HeaderName>: <HeaderType(optional)> <JWT>.
+    #
+    # Also handle the fact that the header that can be comma delimited, ie
+    # <HeaderName>: <field> <value>, <field> <value>, etc...
+    if header_type:
+        field_values = split(r",\s*", auth_header)
+        jwt_headers = [s for s in field_values if s.split()[0] == header_type]
+        if len(jwt_headers) != 1:
+            msg = (
+                f"Missing '{header_type}' type in '{header_name}' header. "
+                f"Expected '{header_name}: {header_type} <JWT>'"
+            )
+            raise NoAuthorizationError(msg)
+
+        parts = jwt_headers[0].split()
+        if len(parts) != 2:
+            msg = (
+                f"Bad {header_name} header. "
+                f"Expected '{header_name}: {header_type} <JWT>'"
+            )
+            raise InvalidHeaderError(msg)
+
+        encoded_token = parts[1]
+    else:
+        parts = auth_header.split()
+        if len(parts) != 1:
+            msg = f"Bad {header_name} header. Expected '{header_name}: <JWT>'"
+            raise InvalidHeaderError(msg)
+
+        encoded_token = parts[0]
+
+    return encoded_token, None
+
+
+def _decode_jwt_from_cookies(refresh: bool) -> Tuple[str, Optional[str]]:
+    if refresh:
+        cookie_key = config.refresh_cookie_name
+        csrf_header_key = config.refresh_csrf_header_name
+        csrf_field_key = config.refresh_csrf_field_name
+    else:
+        cookie_key = config.access_cookie_name
+        csrf_header_key = config.access_csrf_header_name
+        csrf_field_key = config.access_csrf_field_name
+
+    encoded_token = request.cookies.get(cookie_key)
+    if not encoded_token:
+        raise NoAuthorizationError('Missing cookie "{}"'.format(cookie_key))
+
+    if config.csrf_protect and request.method in config.csrf_request_methods:
+        csrf_value = request.headers.get(csrf_header_key, None)
+        if not csrf_value and config.csrf_check_form:
+            csrf_value = request.form.get(csrf_field_key, None)
+        if not csrf_value:
+            raise CSRFError("Missing CSRF token")
+    else:
+        csrf_value = None
+
+    return encoded_token, csrf_value
+
+
+def _decode_jwt_from_query_string() -> Tuple[str, None]:
+    param_name = config.query_string_name
+    prefix = config.query_string_value_prefix
+
+    value = request.args.get(param_name)
+    if not value:
+        raise NoAuthorizationError(f"Missing '{param_name}' query paramater")
+
+    if not value.startswith(prefix):
+        raise InvalidQueryParamError(
+            f"Invalid value for query parameter '{param_name}'. "
+            f"Expected the value to start with '{prefix}'"
+        )
+
+    encoded_token = value[len(prefix) :]  # noqa: E203
+    return encoded_token, None
+
+
+def _decode_jwt_from_json(refresh: bool) -> Tuple[str, None]:
+    if not request.is_json:
+        raise NoAuthorizationError("Invalid content-type. Must be application/json.")
+
+    if refresh:
+        token_key = config.refresh_json_key
+    else:
+        token_key = config.json_key
+
+    try:
+        encoded_token = request.json and request.json.get(token_key, None)
+        if not encoded_token:
+            raise BadRequest()
+    except BadRequest:
+        raise NoAuthorizationError(
+            'Missing "{}" key in json data.'.format(token_key)
+        ) from None
+
+    return encoded_token, None
+
+
+def _decode_jwt_from_request(
+    locations: LocationType,
+    fresh: bool,
+    refresh: bool = False,
+    verify_type: bool = True,
+) -> Tuple[dict, dict, str]:
+    # Figure out what locations to look for the JWT in this request
+    if isinstance(locations, str):
+        locations = [locations]
+
+    if not locations:
+        locations = config.token_location
+
+    # Get the decode functions in the order specified by locations.
+    # Each entry in this list is a tuple (<location>, <encoded-token-function>)
+    get_encoded_token_functions = []
+    for location in locations:
+        if location == "cookies":
+            get_encoded_token_functions.append(
+                (location, lambda: _decode_jwt_from_cookies(refresh))
+            )
+        elif location == "query_string":
+            get_encoded_token_functions.append(
+                (location, _decode_jwt_from_query_string)
+            )
+        elif location == "headers":
+            get_encoded_token_functions.append((location, _decode_jwt_from_headers))
+        elif location == "json":
+            get_encoded_token_functions.append(
+                (location, lambda: _decode_jwt_from_json(refresh))
+            )
+        else:
+            raise RuntimeError(f"'{location}' is not a valid location")
+
+    # Try to find the token from one of these locations. It only needs to exist
+    # in one place to be valid (not every location).
+    errors = []
+    decoded_token = None
+    for location, get_encoded_token_function in get_encoded_token_functions:
+        try:
+            encoded_token, csrf_token = get_encoded_token_function()
+            decoded_token = decode_token(encoded_token, csrf_token)
+            jwt_location = location
+            jwt_header = get_unverified_jwt_headers(encoded_token)
+            break
+        except NoAuthorizationError as e:
+            errors.append(str(e))
+
+    # Do some work to make a helpful and human readable error message if no
+    # token was found in any of the expected locations.
+    if not decoded_token:
+        if len(locations) > 1:
+            err_msg = "Missing JWT in {start_locs} or {end_locs} ({details})".format(
+                start_locs=", ".join(locations[:-1]),
+                end_locs=locations[-1],
+                details="; ".join(errors),
+            )
+            raise NoAuthorizationError(err_msg)
+        else:
+            raise NoAuthorizationError(errors[0])
+
+    # Additional verifications provided by this extension
+    if verify_type:
+        verify_token_type(decoded_token, refresh)
+
+    if fresh:
+        _verify_token_is_fresh(jwt_header, decoded_token)
+    verify_token_not_blocklisted(jwt_header, decoded_token)
+    custom_verification_for_token(jwt_header, decoded_token)
+
+    return decoded_token, jwt_header, jwt_location

TEMPAT SAMPAH
rest_venv/Lib/site-packages/jwt/__pycache__/__init__.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/jwt/__pycache__/algorithms.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/jwt/__pycache__/api_jwk.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/jwt/__pycache__/api_jws.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/jwt/__pycache__/api_jwt.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/jwt/__pycache__/exceptions.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/jwt/__pycache__/help.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/jwt/__pycache__/jwks_client.cpython-39.pyc


TEMPAT SAMPAH
rest_venv/Lib/site-packages/jwt/__pycache__/utils.cpython-39.pyc


+ 1 - 0
rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/INSTALLER

@@ -0,0 +1 @@
+pip

+ 202 - 0
rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/LICENSE

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 217 - 0
rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/METADATA

@@ -0,0 +1,217 @@
+Metadata-Version: 2.1
+Name: pyOpenSSL
+Version: 22.0.0
+Summary: Python wrapper module around the OpenSSL library
+Home-page: https://pyopenssl.org/
+Author: The pyOpenSSL developers
+Author-email: cryptography-dev@python.org
+License: Apache License, Version 2.0
+Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Security :: Cryptography
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Networking
+Requires-Python: >=3.6
+License-File: LICENSE
+Requires-Dist: cryptography (>=35.0)
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
+Provides-Extra: test
+Requires-Dist: flaky ; extra == 'test'
+Requires-Dist: pretend ; extra == 'test'
+Requires-Dist: pytest (>=3.0.1) ; extra == 'test'
+
+========================================================
+pyOpenSSL -- A Python wrapper around the OpenSSL library
+========================================================
+
+.. image:: https://readthedocs.org/projects/pyopenssl/badge/?version=stable
+   :target: https://pyopenssl.org/en/stable/
+   :alt: Stable Docs
+
+.. image:: https://github.com/pyca/pyopenssl/workflows/CI/badge.svg?branch=main
+   :target: https://github.com/pyca/pyopenssl/actions?query=workflow%3ACI+branch%3Amain
+
+.. image:: https://codecov.io/github/pyca/pyopenssl/branch/main/graph/badge.svg
+   :target: https://codecov.io/github/pyca/pyopenssl
+   :alt: Test coverage
+
+**Note:** The Python Cryptographic Authority **strongly suggests** the use of `pyca/cryptography`_
+where possible. If you are using pyOpenSSL for anything other than making a TLS connection
+**you should move to cryptography and drop your pyOpenSSL dependency**.
+
+High-level wrapper around a subset of the OpenSSL library. Includes
+
+* ``SSL.Connection`` objects, wrapping the methods of Python's portable sockets
+* Callbacks written in Python
+* Extensive error-handling mechanism, mirroring OpenSSL's error codes
+
+... and much more.
+
+You can find more information in the documentation_.
+Development takes place on GitHub_.
+
+
+Discussion
+==========
+
+If you run into bugs, you can file them in our `issue tracker`_.
+
+We maintain a cryptography-dev_ mailing list for both user and development discussions.
+
+You can also join ``#cryptography-dev`` on Freenode to ask questions or get involved.
+
+
+.. _documentation: https://pyopenssl.org/
+.. _`issue tracker`: https://github.com/pyca/pyopenssl/issues
+.. _cryptography-dev: https://mail.python.org/mailman/listinfo/cryptography-dev
+.. _GitHub: https://github.com/pyca/pyopenssl
+.. _`pyca/cryptography`: https://github.com/pyca/cryptography
+
+
+Release Information
+===================
+
+22.0.0 (2022-01-29)
+-------------------
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Drop support for Python 2.7.
+  `#1047 <https://github.com/pyca/pyopenssl/pull/1047>`_
+- The minimum ``cryptography`` version is now 35.0.
+
+Deprecations:
+^^^^^^^^^^^^^
+
+Changes:
+^^^^^^^^
+
+- Expose wrappers for some `DTLS
+  <https://en.wikipedia.org/wiki/Datagram_Transport_Layer_Security>`_
+  primitives. `#1026 <https://github.com/pyca/pyopenssl/pull/1026>`_
+
+21.0.0 (2021-09-28)
+-------------------
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- The minimum ``cryptography`` version is now 3.3.
+- Drop support for Python 3.5
+
+Deprecations:
+^^^^^^^^^^^^^
+
+Changes:
+^^^^^^^^
+
+- Raise an error when an invalid ALPN value is set.
+  `#993 <https://github.com/pyca/pyopenssl/pull/993>`_
+- Added ``OpenSSL.SSL.Context.set_min_proto_version`` and ``OpenSSL.SSL.Context.set_max_proto_version``
+  to set the minimum and maximum supported TLS version `#985 <https://github.com/pyca/pyopenssl/pull/985>`_.
+- Updated ``to_cryptography`` and ``from_cryptography`` methods to support an upcoming release of ``cryptography`` without raising deprecation warnings.
+  `#1030 <https://github.com/pyca/pyopenssl/pull/1030>`_
+
+20.0.1 (2020-12-15)
+-------------------
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Deprecations:
+^^^^^^^^^^^^^
+
+Changes:
+^^^^^^^^
+
+- Fixed compatibility with OpenSSL 1.1.0.
+
+20.0.0 (2020-11-27)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- The minimum ``cryptography`` version is now 3.2.
+- Remove deprecated ``OpenSSL.tsafe`` module.
+- Removed deprecated ``OpenSSL.SSL.Context.set_npn_advertise_callback``, ``OpenSSL.SSL.Context.set_npn_select_callback``, and ``OpenSSL.SSL.Connection.get_next_proto_negotiated``.
+- Drop support for Python 3.4
+- Drop support for OpenSSL 1.0.1 and 1.0.2
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- Deprecated ``OpenSSL.crypto.loads_pkcs7`` and ``OpenSSL.crypto.loads_pkcs12``.
+
+Changes:
+^^^^^^^^
+
+- Added a new optional ``chain`` parameter to ``OpenSSL.crypto.X509StoreContext()``
+  where additional untrusted certificates can be specified to help chain building.
+  `#948 <https://github.com/pyca/pyopenssl/pull/948>`_
+- Added ``OpenSSL.crypto.X509Store.load_locations`` to set trusted
+  certificate file bundles and/or directories for verification.
+  `#943 <https://github.com/pyca/pyopenssl/pull/943>`_
+- Added ``Context.set_keylog_callback`` to log key material.
+  `#910 <https://github.com/pyca/pyopenssl/pull/910>`_
+- Added ``OpenSSL.SSL.Connection.get_verified_chain`` to retrieve the
+  verified certificate chain of the peer.
+  `#894 <https://github.com/pyca/pyopenssl/pull/894>`_.
+- Make verification callback optional in ``Context.set_verify``.
+  If omitted, OpenSSL's default verification is used.
+  `#933 <https://github.com/pyca/pyopenssl/pull/933>`_
+- Fixed a bug that could truncate or cause a zero-length key error due to a
+  null byte in private key passphrase in ``OpenSSL.crypto.load_privatekey``
+  and ``OpenSSL.crypto.dump_privatekey``.
+  `#947 <https://github.com/pyca/pyopenssl/pull/947>`_
+
+19.1.0 (2019-11-18)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Removed deprecated ``ContextType``, ``ConnectionType``, ``PKeyType``, ``X509NameType``, ``X509ReqType``, ``X509Type``, ``X509StoreType``, ``CRLType``, ``PKCS7Type``, ``PKCS12Type``, and ``NetscapeSPKIType`` aliases.
+  Use the classes without the ``Type`` suffix instead.
+  `#814 <https://github.com/pyca/pyopenssl/pull/814>`_
+- The minimum ``cryptography`` version is now 2.8 due to issues on macOS with a transitive dependency.
+  `#875 <https://github.com/pyca/pyopenssl/pull/875>`_
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- Deprecated ``OpenSSL.SSL.Context.set_npn_advertise_callback``, ``OpenSSL.SSL.Context.set_npn_select_callback``, and ``OpenSSL.SSL.Connection.get_next_proto_negotiated``.
+  ALPN should be used instead.
+  `#820 <https://github.com/pyca/pyopenssl/pull/820>`_
+
+
+Changes:
+^^^^^^^^
+
+- Support ``bytearray`` in ``SSL.Connection.send()`` by using cffi's from_buffer.
+  `#852 <https://github.com/pyca/pyopenssl/pull/852>`_
+- The ``OpenSSL.SSL.Context.set_alpn_select_callback`` can return a new ``NO_OVERLAPPING_PROTOCOLS`` sentinel value
+  to allow a TLS handshake to complete without an application protocol.
+
+`Full changelog <https://pyopenssl.org/en/stable/changelog.html>`_.
+
+
+

+ 21 - 0
rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/RECORD

@@ -0,0 +1,21 @@
+OpenSSL/SSL.py,sha256=EOcniGt5sU0sfJeHYmfC0syfUu37ffQrUwJrncFcefA,92329
+OpenSSL/__init__.py,sha256=yLB6P6M4uI5LpYpd6H6vk4bON9VBBMauX5UZvp5q3J8,498
+OpenSSL/__pycache__/SSL.cpython-39.pyc,,
+OpenSSL/__pycache__/__init__.cpython-39.pyc,,
+OpenSSL/__pycache__/_util.cpython-39.pyc,,
+OpenSSL/__pycache__/crypto.cpython-39.pyc,,
+OpenSSL/__pycache__/debug.cpython-39.pyc,,
+OpenSSL/__pycache__/rand.cpython-39.pyc,,
+OpenSSL/__pycache__/version.cpython-39.pyc,,
+OpenSSL/_util.py,sha256=HYht7bEO2AXJNZ2HD4K_wFmGNFqVzC5KweHQ577XAdM,3282
+OpenSSL/crypto.py,sha256=6a-blnCZn5R1DwXprLHcwYGqUnjMUlsM21MQ-WpLaVY,105009
+OpenSSL/debug.py,sha256=-8jON6ixVC7vmUQxhttRbTlWZTNd-QDv6T8AFXPW-wA,1047
+OpenSSL/rand.py,sha256=9CGox0RJbrrdH1L7SisEQYhcOp0ygphuI801OzYwSoE,1042
+OpenSSL/version.py,sha256=bLtVCNfx__Fb90Xw3qARCLgHpBHNTVY753wrt3_lc0o,650
+pyOpenSSL-22.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pyOpenSSL-22.0.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
+pyOpenSSL-22.0.0.dist-info/METADATA,sha256=lThz5poivosW3clun59nrOrRH-8S6FscX0DtS__J62U,7805
+pyOpenSSL-22.0.0.dist-info/RECORD,,
+pyOpenSSL-22.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pyOpenSSL-22.0.0.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
+pyOpenSSL-22.0.0.dist-info/top_level.txt,sha256=NNxWqS8hKNJh2cUXa1RZOMX62VJfyd8URo1TsYnR_MU,8

+ 0 - 0
rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/REQUESTED


+ 6 - 0
rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/WHEEL

@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+

+ 1 - 0
rest_venv/Lib/site-packages/pyOpenSSL-22.0.0.dist-info/top_level.txt

@@ -0,0 +1 @@
+OpenSSL