diff --git a/django/core/signing.py b/django/core/signing.py
index cd9759e..61faee2 100644
a
|
b
|
start of the base64 JSON.
|
32 | 32 | There are 65 url-safe characters: the 64 used by url-safe base64 and the ':'. |
33 | 33 | These functions make use of all of them. |
34 | 34 | """ |
| 35 | |
| 36 | from __future__ import unicode_literals |
| 37 | |
35 | 38 | import base64 |
36 | 39 | import json |
37 | 40 | import time |
… |
… |
from django.conf import settings
|
41 | 44 | from django.core.exceptions import ImproperlyConfigured |
42 | 45 | from django.utils import baseconv |
43 | 46 | from django.utils.crypto import constant_time_compare, salted_hmac |
44 | | from django.utils.encoding import force_unicode, smart_str |
| 47 | from django.utils.encoding import force_str, force_text |
45 | 48 | from django.utils.importlib import import_module |
46 | 49 | |
47 | 50 | |
… |
… |
class SignatureExpired(BadSignature):
|
60 | 63 | |
61 | 64 | |
62 | 65 | def b64_encode(s): |
63 | | return base64.urlsafe_b64encode(s).strip('=') |
| 66 | return base64.urlsafe_b64encode(s).strip(b'=') |
64 | 67 | |
65 | 68 | |
66 | 69 | def b64_decode(s): |
67 | | pad = '=' * (-len(s) % 4) |
| 70 | pad = b'=' * (-len(s) % 4) |
68 | 71 | return base64.urlsafe_b64decode(s + pad) |
69 | 72 | |
70 | 73 | |
… |
… |
def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer,
|
114 | 117 | value or re-using a salt value across different parts of your |
115 | 118 | application without good cause is a security risk. |
116 | 119 | """ |
117 | | data = serializer().dumps(obj) |
| 120 | data = serializer().dumps(obj).encode() |
118 | 121 | |
119 | 122 | # Flag for if it's been compressed or not |
120 | 123 | is_compressed = False |
… |
… |
def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer,
|
127 | 130 | is_compressed = True |
128 | 131 | base64d = b64_encode(data) |
129 | 132 | if is_compressed: |
130 | | base64d = '.' + base64d |
| 133 | base64d = b'.' + base64d |
131 | 134 | return TimestampSigner(key, salt=salt).sign(base64d) |
132 | 135 | |
133 | 136 | |
… |
… |
def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, ma
|
135 | 138 | """ |
136 | 139 | Reverse of dumps(), raises BadSignature if signature fails |
137 | 140 | """ |
138 | | base64d = smart_str( |
139 | | TimestampSigner(key, salt=salt).unsign(s, max_age=max_age)) |
| 141 | # TimestampSigner.unsign always returns unicode but base64 and zlib |
| 142 | # compression operate on bytes, so we decode() here. |
| 143 | base64d = TimestampSigner(key, salt=salt).unsign(s, max_age=max_age).encode() |
140 | 144 | decompress = False |
141 | | if base64d[0] == '.': |
| 145 | if base64d[0] == b'.': |
142 | 146 | # It's compressed; uncompress it first |
143 | 147 | base64d = base64d[1:] |
144 | 148 | decompress = True |
145 | 149 | data = b64_decode(base64d) |
146 | 150 | if decompress: |
147 | 151 | data = zlib.decompress(data) |
148 | | return serializer().loads(data) |
| 152 | return serializer().loads(data.decode()) |
149 | 153 | |
150 | 154 | |
151 | 155 | class Signer(object): |
| 156 | |
152 | 157 | def __init__(self, key=None, sep=':', salt=None): |
153 | | self.sep = sep |
154 | | self.key = key or settings.SECRET_KEY |
155 | | self.salt = salt or ('%s.%s' % |
156 | | (self.__class__.__module__, self.__class__.__name__)) |
| 158 | # Use of native strings in all versions of Python |
| 159 | self.sep = str(sep) |
| 160 | self.key = str(key or settings.SECRET_KEY) |
| 161 | self.salt = str(salt or |
| 162 | '%s.%s' % (self.__class__.__module__, self.__class__.__name__)) |
157 | 163 | |
158 | 164 | def signature(self, value): |
159 | | return base64_hmac(self.salt + 'signer', value, self.key) |
| 165 | signature = base64_hmac(self.salt + 'signer', value, self.key) |
| 166 | # Convert the signature from bytes to str only on Python 3 |
| 167 | return force_str(signature) |
160 | 168 | |
161 | 169 | def sign(self, value): |
162 | | value = smart_str(value) |
163 | | return '%s%s%s' % (value, self.sep, self.signature(value)) |
| 170 | value = force_str(value) |
| 171 | return str('%s%s%s') % (value, self.sep, self.signature(value)) |
164 | 172 | |
165 | 173 | def unsign(self, signed_value): |
166 | | signed_value = smart_str(signed_value) |
| 174 | signed_value = force_str(signed_value) |
167 | 175 | if not self.sep in signed_value: |
168 | 176 | raise BadSignature('No "%s" found in value' % self.sep) |
169 | 177 | value, sig = signed_value.rsplit(self.sep, 1) |
170 | 178 | if constant_time_compare(sig, self.signature(value)): |
171 | | return force_unicode(value) |
| 179 | return force_text(value) |
172 | 180 | raise BadSignature('Signature "%s" does not match' % sig) |
173 | 181 | |
174 | 182 | |
… |
… |
class TimestampSigner(Signer):
|
178 | 186 | return baseconv.base62.encode(int(time.time())) |
179 | 187 | |
180 | 188 | def sign(self, value): |
181 | | value = smart_str('%s%s%s' % (value, self.sep, self.timestamp())) |
182 | | return '%s%s%s' % (value, self.sep, self.signature(value)) |
| 189 | value = force_str(value) |
| 190 | value = str('%s%s%s') % (value, self.sep, self.timestamp()) |
| 191 | return super(TimestampSigner, self).sign(value) |
183 | 192 | |
184 | 193 | def unsign(self, value, max_age=None): |
185 | 194 | result = super(TimestampSigner, self).unsign(value) |