We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Key versioning/key rotation support (wishlist)
It would be nice if Fernet had built-in support for key versioning.  I don't know exactly what the API would look like, but the idea would be to allow encrypting new data under a new key while still retaining the ability to decrypt old data that was encrypted under older keys.

If Fernet supported this natively, then I could tell developers to "just use Fernet" when they first write their applications, and later when they get a real security engineer, that person could start doing key rotation without having to change application-level code or data structures.


--- END ISSUE ---

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

{"golden_diff": "diff --git a/cryptography/fernet.py b/cryptography/fernet.py\n--- a/cryptography/fernet.py\n+++ b/cryptography/fernet.py\n@@ -127,3 +127,24 @@\n         except ValueError:\n             raise InvalidToken\n         return unpadded\n+\n+\n+class 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", "issue": "Key versioning/key rotation support (wishlist)\nIt would be nice if Fernet had built-in support for key versioning.  I don't know exactly what the API would look like, but the idea would be to allow encrypting new data under a new key while still retaining the ability to decrypt old data that was encrypted under older keys.\n\nIf Fernet supported this natively, then I could tell developers to \"just use Fernet\" when they first write their applications, and later when they get a real security engineer, that person could start doing key rotation without having to change application-level code or data structures.\n\n", "before_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n# implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\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", "path": "cryptography/fernet.py"}], "after_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n# implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\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": "cryptography/fernet.py"}]}