We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Any interest for an override parameter for _MAX_CLOCK_SKEW?
It seems like a narrow assumption to assume that the actors communicating via Fernet-encrypted messages will always be able to have somewhat synchronized clocks. In our case, we have no way to amend the clocks of the systems upon which our software runs and they often differ by at least minutes, if not hours or days.

Our current solution is to just stomp on `cryptography.fernet._MAX_CLOCK_SKEW` and set it to some `LARGE_VALUE`. Not ideal, but it gets the job done for now.

I can think of several options to improve the situation here, but most of them involve changes to this library's code.

1) Add an optional parameter to Fernet's `__init__()` function or `decrypt()` function to disable the clock skew check altogether.
2) Add an optional parameter to Fernet's `__init__()` function or `decrypt()` that defaults to `_MAX_CLOCK_SKEW` and use that value for the clock skew check.
3) Add ability to set an offset on the Fernet object (or pass it in to `decrypt()`) that would be applied to the extracted timestamp prior to being checked against `_MAX_CLOCK_SKEW`. It would be the library user's responsibility for determining the appropriate offset value for each external actor.
4) I could subclass Fernet and implement some appropriate behavior in my new class (would essentially involve copying the whole `decrypt()` function into my codebase just to tweak this specific portion's behavior)

Would any of options 1-3 be worth pursuing to the point of a pull-request? I can probably make a pretty good effort on each of them, but I don't want to spend the time if the maintainers don't think any are worth attempting.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/cryptography/fernet.py`
Content:
```
1 # This file is dual licensed under the terms of the Apache License, Version
2 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3 # for complete details.
4 
5 from __future__ import absolute_import, division, print_function
6 
7 import base64
8 import binascii
9 import os
10 import struct
11 import time
12 
13 import six
14 
15 from cryptography.exceptions import InvalidSignature
16 from cryptography.hazmat.backends import default_backend
17 from cryptography.hazmat.primitives import hashes, padding
18 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
19 from cryptography.hazmat.primitives.hmac import HMAC
20 
21 
22 class InvalidToken(Exception):
23     pass
24 
25 
26 _MAX_CLOCK_SKEW = 60
27 
28 
29 class Fernet(object):
30     def __init__(self, key, backend=None):
31         if backend is None:
32             backend = default_backend()
33 
34         key = base64.urlsafe_b64decode(key)
35         if len(key) != 32:
36             raise ValueError(
37                 "Fernet key must be 32 url-safe base64-encoded bytes."
38             )
39 
40         self._signing_key = key[:16]
41         self._encryption_key = key[16:]
42         self._backend = backend
43 
44     @classmethod
45     def generate_key(cls):
46         return base64.urlsafe_b64encode(os.urandom(32))
47 
48     def encrypt(self, data):
49         current_time = int(time.time())
50         iv = os.urandom(16)
51         return self._encrypt_from_parts(data, current_time, iv)
52 
53     def _encrypt_from_parts(self, data, current_time, iv):
54         if not isinstance(data, bytes):
55             raise TypeError("data must be bytes.")
56 
57         padder = padding.PKCS7(algorithms.AES.block_size).padder()
58         padded_data = padder.update(data) + padder.finalize()
59         encryptor = Cipher(
60             algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend
61         ).encryptor()
62         ciphertext = encryptor.update(padded_data) + encryptor.finalize()
63 
64         basic_parts = (
65             b"\x80" + struct.pack(">Q", current_time) + iv + ciphertext
66         )
67 
68         h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)
69         h.update(basic_parts)
70         hmac = h.finalize()
71         return base64.urlsafe_b64encode(basic_parts + hmac)
72 
73     def decrypt(self, token, ttl=None):
74         if not isinstance(token, bytes):
75             raise TypeError("token must be bytes.")
76 
77         current_time = int(time.time())
78 
79         try:
80             data = base64.urlsafe_b64decode(token)
81         except (TypeError, binascii.Error):
82             raise InvalidToken
83 
84         if not data or six.indexbytes(data, 0) != 0x80:
85             raise InvalidToken
86 
87         try:
88             timestamp, = struct.unpack(">Q", data[1:9])
89         except struct.error:
90             raise InvalidToken
91         if ttl is not None:
92             if timestamp + ttl < current_time:
93                 raise InvalidToken
94         if current_time + _MAX_CLOCK_SKEW < timestamp:
95             raise InvalidToken
96         h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)
97         h.update(data[:-32])
98         try:
99             h.verify(data[-32:])
100         except InvalidSignature:
101             raise InvalidToken
102 
103         iv = data[9:25]
104         ciphertext = data[25:-32]
105         decryptor = Cipher(
106             algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend
107         ).decryptor()
108         plaintext_padded = decryptor.update(ciphertext)
109         try:
110             plaintext_padded += decryptor.finalize()
111         except ValueError:
112             raise InvalidToken
113         unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
114 
115         unpadded = unpadder.update(plaintext_padded)
116         try:
117             unpadded += unpadder.finalize()
118         except ValueError:
119             raise InvalidToken
120         return unpadded
121 
122 
123 class MultiFernet(object):
124     def __init__(self, fernets):
125         fernets = list(fernets)
126         if not fernets:
127             raise ValueError(
128                 "MultiFernet requires at least one Fernet instance"
129             )
130         self._fernets = fernets
131 
132     def encrypt(self, msg):
133         return self._fernets[0].encrypt(msg)
134 
135     def decrypt(self, msg, ttl=None):
136         for f in self._fernets:
137             try:
138                 return f.decrypt(msg, ttl)
139             except InvalidToken:
140                 pass
141         raise InvalidToken
142
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/cryptography/fernet.py b/src/cryptography/fernet.py
--- a/src/cryptography/fernet.py
+++ b/src/cryptography/fernet.py
@@ -91,8 +91,10 @@
         if ttl is not None:
             if timestamp + ttl < current_time:
                 raise InvalidToken
-        if current_time + _MAX_CLOCK_SKEW < timestamp:
-            raise InvalidToken
+
+            if current_time + _MAX_CLOCK_SKEW < timestamp:
+                raise InvalidToken
+
         h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)
         h.update(data[:-32])
         try:

{"golden_diff": "diff --git a/src/cryptography/fernet.py b/src/cryptography/fernet.py\n--- a/src/cryptography/fernet.py\n+++ b/src/cryptography/fernet.py\n@@ -91,8 +91,10 @@\n         if ttl is not None:\n             if timestamp + ttl < current_time:\n                 raise InvalidToken\n-        if current_time + _MAX_CLOCK_SKEW < timestamp:\n-            raise InvalidToken\n+\n+            if current_time + _MAX_CLOCK_SKEW < timestamp:\n+                raise InvalidToken\n+\n         h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)\n         h.update(data[:-32])\n         try:\n", "issue": "Any interest for an override parameter for _MAX_CLOCK_SKEW?\nIt seems like a narrow assumption to assume that the actors communicating via Fernet-encrypted messages will always be able to have somewhat synchronized clocks. In our case, we have no way to amend the clocks of the systems upon which our software runs and they often differ by at least minutes, if not hours or days.\n\nOur current solution is to just stomp on `cryptography.fernet._MAX_CLOCK_SKEW` and set it to some `LARGE_VALUE`. Not ideal, but it gets the job done for now.\n\nI can think of several options to improve the situation here, but most of them involve changes to this library's code.\n\n1) Add an optional parameter to Fernet's `__init__()` function or `decrypt()` function to disable the clock skew check altogether.\n2) Add an optional parameter to Fernet's `__init__()` function or `decrypt()` that defaults to `_MAX_CLOCK_SKEW` and use that value for the clock skew check.\n3) Add ability to set an offset on the Fernet object (or pass it in to `decrypt()`) that would be applied to the extracted timestamp prior to being checked against `_MAX_CLOCK_SKEW`. It would be the library user's responsibility for determining the appropriate offset value for each external actor.\n4) I could subclass Fernet and implement some appropriate behavior in my new class (would essentially involve copying the whole `decrypt()` function into my codebase just to tweak this specific portion's behavior)\n\nWould any of options 1-3 be worth pursuing to the point of a pull-request? I can probably make a pretty good effort on each of them, but I don't want to spend the time if the maintainers don't think any are worth attempting.\n\n", "before_files": [{"content": "# This file is dual licensed under the terms of the Apache License, Version\n# 2.0, and the BSD License. See the LICENSE file in the root of this repository\n# for complete details.\n\nfrom __future__ import absolute_import, division, print_function\n\nimport base64\nimport binascii\nimport os\nimport struct\nimport time\n\nimport six\n\nfrom cryptography.exceptions import InvalidSignature\nfrom cryptography.hazmat.backends import default_backend\nfrom cryptography.hazmat.primitives import hashes, padding\nfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes\nfrom cryptography.hazmat.primitives.hmac import HMAC\n\n\nclass InvalidToken(Exception):\n    pass\n\n\n_MAX_CLOCK_SKEW = 60\n\n\nclass Fernet(object):\n    def __init__(self, key, backend=None):\n        if backend is None:\n            backend = default_backend()\n\n        key = base64.urlsafe_b64decode(key)\n        if len(key) != 32:\n            raise ValueError(\n                \"Fernet key must be 32 url-safe base64-encoded bytes.\"\n            )\n\n        self._signing_key = key[:16]\n        self._encryption_key = key[16:]\n        self._backend = backend\n\n    @classmethod\n    def generate_key(cls):\n        return base64.urlsafe_b64encode(os.urandom(32))\n\n    def encrypt(self, data):\n        current_time = int(time.time())\n        iv = os.urandom(16)\n        return self._encrypt_from_parts(data, current_time, iv)\n\n    def _encrypt_from_parts(self, data, current_time, iv):\n        if not isinstance(data, bytes):\n            raise TypeError(\"data must be bytes.\")\n\n        padder = padding.PKCS7(algorithms.AES.block_size).padder()\n        padded_data = padder.update(data) + padder.finalize()\n        encryptor = Cipher(\n            algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend\n        ).encryptor()\n        ciphertext = encryptor.update(padded_data) + encryptor.finalize()\n\n        basic_parts = (\n            b\"\\x80\" + struct.pack(\">Q\", current_time) + iv + ciphertext\n        )\n\n        h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)\n        h.update(basic_parts)\n        hmac = h.finalize()\n        return base64.urlsafe_b64encode(basic_parts + hmac)\n\n    def decrypt(self, token, ttl=None):\n        if not isinstance(token, bytes):\n            raise TypeError(\"token must be bytes.\")\n\n        current_time = int(time.time())\n\n        try:\n            data = base64.urlsafe_b64decode(token)\n        except (TypeError, binascii.Error):\n            raise InvalidToken\n\n        if not data or six.indexbytes(data, 0) != 0x80:\n            raise InvalidToken\n\n        try:\n            timestamp, = struct.unpack(\">Q\", data[1:9])\n        except struct.error:\n            raise InvalidToken\n        if ttl is not None:\n            if timestamp + ttl < current_time:\n                raise InvalidToken\n        if current_time + _MAX_CLOCK_SKEW < timestamp:\n            raise InvalidToken\n        h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)\n        h.update(data[:-32])\n        try:\n            h.verify(data[-32:])\n        except InvalidSignature:\n            raise InvalidToken\n\n        iv = data[9:25]\n        ciphertext = data[25:-32]\n        decryptor = Cipher(\n            algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend\n        ).decryptor()\n        plaintext_padded = decryptor.update(ciphertext)\n        try:\n            plaintext_padded += decryptor.finalize()\n        except ValueError:\n            raise InvalidToken\n        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()\n\n        unpadded = unpadder.update(plaintext_padded)\n        try:\n            unpadded += unpadder.finalize()\n        except ValueError:\n            raise InvalidToken\n        return unpadded\n\n\nclass MultiFernet(object):\n    def __init__(self, fernets):\n        fernets = list(fernets)\n        if not fernets:\n            raise ValueError(\n                \"MultiFernet requires at least one Fernet instance\"\n            )\n        self._fernets = fernets\n\n    def encrypt(self, msg):\n        return self._fernets[0].encrypt(msg)\n\n    def decrypt(self, msg, ttl=None):\n        for f in self._fernets:\n            try:\n                return f.decrypt(msg, ttl)\n            except InvalidToken:\n                pass\n        raise InvalidToken\n", "path": "src/cryptography/fernet.py"}], "after_files": [{"content": "# This file is dual licensed under the terms of the Apache License, Version\n# 2.0, and the BSD License. See the LICENSE file in the root of this repository\n# for complete details.\n\nfrom __future__ import absolute_import, division, print_function\n\nimport base64\nimport binascii\nimport os\nimport struct\nimport time\n\nimport six\n\nfrom cryptography.exceptions import InvalidSignature\nfrom cryptography.hazmat.backends import default_backend\nfrom cryptography.hazmat.primitives import hashes, padding\nfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes\nfrom cryptography.hazmat.primitives.hmac import HMAC\n\n\nclass InvalidToken(Exception):\n    pass\n\n\n_MAX_CLOCK_SKEW = 60\n\n\nclass Fernet(object):\n    def __init__(self, key, backend=None):\n        if backend is None:\n            backend = default_backend()\n\n        key = base64.urlsafe_b64decode(key)\n        if len(key) != 32:\n            raise ValueError(\n                \"Fernet key must be 32 url-safe base64-encoded bytes.\"\n            )\n\n        self._signing_key = key[:16]\n        self._encryption_key = key[16:]\n        self._backend = backend\n\n    @classmethod\n    def generate_key(cls):\n        return base64.urlsafe_b64encode(os.urandom(32))\n\n    def encrypt(self, data):\n        current_time = int(time.time())\n        iv = os.urandom(16)\n        return self._encrypt_from_parts(data, current_time, iv)\n\n    def _encrypt_from_parts(self, data, current_time, iv):\n        if not isinstance(data, bytes):\n            raise TypeError(\"data must be bytes.\")\n\n        padder = padding.PKCS7(algorithms.AES.block_size).padder()\n        padded_data = padder.update(data) + padder.finalize()\n        encryptor = Cipher(\n            algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend\n        ).encryptor()\n        ciphertext = encryptor.update(padded_data) + encryptor.finalize()\n\n        basic_parts = (\n            b\"\\x80\" + struct.pack(\">Q\", current_time) + iv + ciphertext\n        )\n\n        h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)\n        h.update(basic_parts)\n        hmac = h.finalize()\n        return base64.urlsafe_b64encode(basic_parts + hmac)\n\n    def decrypt(self, token, ttl=None):\n        if not isinstance(token, bytes):\n            raise TypeError(\"token must be bytes.\")\n\n        current_time = int(time.time())\n\n        try:\n            data = base64.urlsafe_b64decode(token)\n        except (TypeError, binascii.Error):\n            raise InvalidToken\n\n        if not data or six.indexbytes(data, 0) != 0x80:\n            raise InvalidToken\n\n        try:\n            timestamp, = struct.unpack(\">Q\", data[1:9])\n        except struct.error:\n            raise InvalidToken\n        if ttl is not None:\n            if timestamp + ttl < current_time:\n                raise InvalidToken\n\n            if current_time + _MAX_CLOCK_SKEW < timestamp:\n                raise InvalidToken\n\n        h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)\n        h.update(data[:-32])\n        try:\n            h.verify(data[-32:])\n        except InvalidSignature:\n            raise InvalidToken\n\n        iv = data[9:25]\n        ciphertext = data[25:-32]\n        decryptor = Cipher(\n            algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend\n        ).decryptor()\n        plaintext_padded = decryptor.update(ciphertext)\n        try:\n            plaintext_padded += decryptor.finalize()\n        except ValueError:\n            raise InvalidToken\n        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()\n\n        unpadded = unpadder.update(plaintext_padded)\n        try:\n            unpadded += unpadder.finalize()\n        except ValueError:\n            raise InvalidToken\n        return unpadded\n\n\nclass MultiFernet(object):\n    def __init__(self, fernets):\n        fernets = list(fernets)\n        if not fernets:\n            raise ValueError(\n                \"MultiFernet requires at least one Fernet instance\"\n            )\n        self._fernets = fernets\n\n    def encrypt(self, msg):\n        return self._fernets[0].encrypt(msg)\n\n    def decrypt(self, msg, ttl=None):\n        for f in self._fernets:\n            try:\n                return f.decrypt(msg, ttl)\n            except InvalidToken:\n                pass\n        raise InvalidToken\n", "path": "src/cryptography/fernet.py"}]}