2014-11-15 18:03:04 +08:00
|
|
|
import datetime
|
2011-06-17 17:47:08 +08:00
|
|
|
|
2011-05-21 22:41:14 +08:00
|
|
|
from django.core import signing
|
2021-12-14 11:47:03 +08:00
|
|
|
from django.test import SimpleTestCase, override_settings
|
2021-01-14 17:27:04 +08:00
|
|
|
from django.test.utils import freeze_time
|
2020-02-14 03:55:48 +08:00
|
|
|
from django.utils.crypto import InvalidAlgorithm
|
2011-05-21 22:41:14 +08:00
|
|
|
|
2011-10-14 05:34:56 +08:00
|
|
|
|
2015-04-18 05:38:20 +08:00
|
|
|
class TestSigner(SimpleTestCase):
|
2011-05-21 22:41:14 +08:00
|
|
|
def test_signature(self):
|
|
|
|
"signature() method should generate a signature"
|
|
|
|
signer = signing.Signer("predictable-secret")
|
|
|
|
signer2 = signing.Signer("predictable-secret2")
|
|
|
|
for s in (
|
2012-05-19 23:43:34 +08:00
|
|
|
b"hello",
|
|
|
|
b"3098247:529:087:",
|
2017-02-08 01:05:47 +08:00
|
|
|
"\u2019".encode(),
|
2011-05-21 22:41:14 +08:00
|
|
|
):
|
|
|
|
self.assertEqual(
|
|
|
|
signer.signature(s),
|
2020-02-14 03:55:48 +08:00
|
|
|
signing.base64_hmac(
|
|
|
|
signer.salt + "signer",
|
|
|
|
s,
|
|
|
|
"predictable-secret",
|
|
|
|
algorithm=signer.algorithm,
|
|
|
|
),
|
2011-05-21 22:41:14 +08:00
|
|
|
)
|
|
|
|
self.assertNotEqual(signer.signature(s), signer2.signature(s))
|
|
|
|
|
|
|
|
def test_signature_with_salt(self):
|
|
|
|
"signature(value, salt=...) should work"
|
|
|
|
signer = signing.Signer("predictable-secret", salt="extra-salt")
|
|
|
|
self.assertEqual(
|
|
|
|
signer.signature("hello"),
|
2020-02-14 03:55:48 +08:00
|
|
|
signing.base64_hmac(
|
|
|
|
"extra-salt" + "signer",
|
|
|
|
"hello",
|
|
|
|
"predictable-secret",
|
|
|
|
algorithm=signer.algorithm,
|
|
|
|
),
|
2013-10-18 17:02:43 +08:00
|
|
|
)
|
2011-05-21 22:41:14 +08:00
|
|
|
self.assertNotEqual(
|
|
|
|
signing.Signer("predictable-secret", salt="one").signature("hello"),
|
|
|
|
signing.Signer("predictable-secret", salt="two").signature("hello"),
|
|
|
|
)
|
|
|
|
|
2020-02-14 03:55:48 +08:00
|
|
|
def test_custom_algorithm(self):
|
|
|
|
signer = signing.Signer("predictable-secret", algorithm="sha512")
|
|
|
|
self.assertEqual(
|
|
|
|
signer.signature("hello"),
|
|
|
|
"Usf3uVQOZ9m6uPfVonKR-EBXjPe7bjMbp3_Fq8MfsptgkkM1ojidN0BxYaT5HAEN1"
|
|
|
|
"VzO9_jVu7R-VkqknHYNvw",
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_invalid_algorithm(self):
|
|
|
|
signer = signing.Signer("predictable-secret", algorithm="whatever")
|
|
|
|
msg = "'whatever' is not an algorithm accepted by the hashlib module."
|
|
|
|
with self.assertRaisesMessage(InvalidAlgorithm, msg):
|
|
|
|
signer.sign("hello")
|
|
|
|
|
2011-05-21 22:41:14 +08:00
|
|
|
def test_sign_unsign(self):
|
|
|
|
"sign/unsign should be reversible"
|
|
|
|
signer = signing.Signer("predictable-secret")
|
2012-08-25 19:02:52 +08:00
|
|
|
examples = [
|
2011-05-21 22:41:14 +08:00
|
|
|
"q;wjmbk;wkmb",
|
|
|
|
"3098247529087",
|
|
|
|
"3098247:529:087:",
|
|
|
|
"jkw osanteuh ,rcuh nthu aou oauh ,ud du",
|
2012-06-08 00:08:47 +08:00
|
|
|
"\u2019",
|
2012-08-25 19:02:52 +08:00
|
|
|
]
|
2011-05-21 22:41:14 +08:00
|
|
|
for example in examples:
|
2012-08-25 19:02:52 +08:00
|
|
|
signed = signer.sign(example)
|
|
|
|
self.assertIsInstance(signed, str)
|
2017-01-12 06:17:25 +08:00
|
|
|
self.assertNotEqual(example, signed)
|
2012-08-25 19:02:52 +08:00
|
|
|
self.assertEqual(example, signer.unsign(signed))
|
2011-05-21 22:41:14 +08:00
|
|
|
|
2020-01-30 17:31:47 +08:00
|
|
|
def test_sign_unsign_non_string(self):
|
|
|
|
signer = signing.Signer("predictable-secret")
|
|
|
|
values = [
|
|
|
|
123,
|
|
|
|
1.23,
|
|
|
|
True,
|
|
|
|
datetime.date.today(),
|
|
|
|
]
|
|
|
|
for value in values:
|
|
|
|
with self.subTest(value):
|
|
|
|
signed = signer.sign(value)
|
|
|
|
self.assertIsInstance(signed, str)
|
|
|
|
self.assertNotEqual(signed, value)
|
|
|
|
self.assertEqual(signer.unsign(signed), str(value))
|
|
|
|
|
2016-03-06 16:48:06 +08:00
|
|
|
def test_unsign_detects_tampering(self):
|
2011-05-21 22:41:14 +08:00
|
|
|
"unsign should raise an exception if the value has been tampered with"
|
|
|
|
signer = signing.Signer("predictable-secret")
|
|
|
|
value = "Another string"
|
|
|
|
signed_value = signer.sign(value)
|
|
|
|
transforms = (
|
|
|
|
lambda s: s.upper(),
|
|
|
|
lambda s: s + "a",
|
|
|
|
lambda s: "a" + s[1:],
|
|
|
|
lambda s: s.replace(":", ""),
|
|
|
|
)
|
|
|
|
self.assertEqual(value, signer.unsign(signed_value))
|
|
|
|
for transform in transforms:
|
2016-01-17 19:26:39 +08:00
|
|
|
with self.assertRaises(signing.BadSignature):
|
|
|
|
signer.unsign(transform(signed_value))
|
2011-05-21 22:41:14 +08:00
|
|
|
|
2020-12-19 21:21:12 +08:00
|
|
|
def test_sign_unsign_object(self):
|
|
|
|
signer = signing.Signer("predictable-secret")
|
|
|
|
tests = [
|
|
|
|
["a", "list"],
|
|
|
|
"a string \u2019",
|
|
|
|
{"a": "dictionary"},
|
|
|
|
]
|
|
|
|
for obj in tests:
|
|
|
|
with self.subTest(obj=obj):
|
|
|
|
signed_obj = signer.sign_object(obj)
|
|
|
|
self.assertNotEqual(obj, signed_obj)
|
|
|
|
self.assertEqual(obj, signer.unsign_object(signed_obj))
|
|
|
|
signed_obj = signer.sign_object(obj, compress=True)
|
|
|
|
self.assertNotEqual(obj, signed_obj)
|
|
|
|
self.assertEqual(obj, signer.unsign_object(signed_obj))
|
|
|
|
|
2011-05-21 22:41:14 +08:00
|
|
|
def test_dumps_loads(self):
|
|
|
|
"dumps and loads be reversible for any JSON serializable object"
|
2012-08-10 02:08:47 +08:00
|
|
|
objects = [
|
2011-05-21 22:41:14 +08:00
|
|
|
["a", "list"],
|
2017-01-21 05:04:05 +08:00
|
|
|
"a string \u2019",
|
2011-05-21 22:41:14 +08:00
|
|
|
{"a": "dictionary"},
|
2012-08-10 02:08:47 +08:00
|
|
|
]
|
2011-05-21 22:41:14 +08:00
|
|
|
for o in objects:
|
|
|
|
self.assertNotEqual(o, signing.dumps(o))
|
|
|
|
self.assertEqual(o, signing.loads(signing.dumps(o)))
|
2012-08-10 02:08:47 +08:00
|
|
|
self.assertNotEqual(o, signing.dumps(o, compress=True))
|
|
|
|
self.assertEqual(o, signing.loads(signing.dumps(o, compress=True)))
|
2011-05-21 22:41:14 +08:00
|
|
|
|
|
|
|
def test_decode_detects_tampering(self):
|
|
|
|
"loads should raise exception for tampered objects"
|
|
|
|
transforms = (
|
|
|
|
lambda s: s.upper(),
|
|
|
|
lambda s: s + "a",
|
|
|
|
lambda s: "a" + s[1:],
|
|
|
|
lambda s: s.replace(":", ""),
|
|
|
|
)
|
|
|
|
value = {
|
|
|
|
"foo": "bar",
|
|
|
|
"baz": 1,
|
|
|
|
}
|
|
|
|
encoded = signing.dumps(value)
|
|
|
|
self.assertEqual(value, signing.loads(encoded))
|
|
|
|
for transform in transforms:
|
2016-01-17 19:26:39 +08:00
|
|
|
with self.assertRaises(signing.BadSignature):
|
|
|
|
signing.loads(transform(encoded))
|
2011-05-21 22:41:14 +08:00
|
|
|
|
2014-02-16 21:47:51 +08:00
|
|
|
def test_works_with_non_ascii_keys(self):
|
|
|
|
binary_key = b"\xe7" # Set some binary (non-ASCII key)
|
|
|
|
|
|
|
|
s = signing.Signer(binary_key)
|
2020-02-14 03:55:48 +08:00
|
|
|
self.assertEqual(
|
|
|
|
"foo:EE4qGC5MEKyQG5msxYA0sBohAxLC0BJf8uRhemh0BGU",
|
|
|
|
s.sign("foo"),
|
|
|
|
)
|
2014-02-16 21:47:51 +08:00
|
|
|
|
2014-06-10 06:15:21 +08:00
|
|
|
def test_valid_sep(self):
|
|
|
|
separators = ["/", "*sep*", ","]
|
|
|
|
for sep in separators:
|
|
|
|
signer = signing.Signer("predictable-secret", sep=sep)
|
2020-02-14 03:55:48 +08:00
|
|
|
self.assertEqual(
|
|
|
|
"foo%sjZQoX_FtSO70jX9HLRGg2A_2s4kdDBxz1QoO_OpEQb0" % sep,
|
|
|
|
signer.sign("foo"),
|
|
|
|
)
|
2014-06-10 06:15:21 +08:00
|
|
|
|
|
|
|
def test_invalid_sep(self):
|
|
|
|
"""should warn on invalid separator"""
|
2015-09-05 22:31:09 +08:00
|
|
|
msg = (
|
|
|
|
"Unsafe Signer separator: %r (cannot be empty or consist of only A-z0-9-_=)"
|
2022-02-04 03:24:19 +08:00
|
|
|
)
|
2014-06-10 06:15:21 +08:00
|
|
|
separators = ["", "-", "abc"]
|
|
|
|
for sep in separators:
|
2015-09-05 22:31:09 +08:00
|
|
|
with self.assertRaisesMessage(ValueError, msg % sep):
|
2014-06-10 06:15:21 +08:00
|
|
|
signing.Signer(sep=sep)
|
|
|
|
|
2021-12-14 11:47:03 +08:00
|
|
|
def test_verify_with_non_default_key(self):
|
|
|
|
old_signer = signing.Signer("secret")
|
|
|
|
new_signer = signing.Signer(
|
|
|
|
"newsecret", fallback_keys=["othersecret", "secret"]
|
|
|
|
)
|
|
|
|
signed = old_signer.sign("abc")
|
|
|
|
self.assertEqual(new_signer.unsign(signed), "abc")
|
|
|
|
|
|
|
|
def test_sign_unsign_multiple_keys(self):
|
|
|
|
"""The default key is a valid verification key."""
|
|
|
|
signer = signing.Signer("secret", fallback_keys=["oldsecret"])
|
|
|
|
signed = signer.sign("abc")
|
|
|
|
self.assertEqual(signer.unsign(signed), "abc")
|
|
|
|
|
|
|
|
@override_settings(
|
|
|
|
SECRET_KEY="secret",
|
|
|
|
SECRET_KEY_FALLBACKS=["oldsecret"],
|
|
|
|
)
|
|
|
|
def test_sign_unsign_ignore_secret_key_fallbacks(self):
|
|
|
|
old_signer = signing.Signer("oldsecret")
|
|
|
|
signed = old_signer.sign("abc")
|
|
|
|
signer = signing.Signer(fallback_keys=[])
|
|
|
|
with self.assertRaises(signing.BadSignature):
|
|
|
|
signer.unsign(signed)
|
|
|
|
|
|
|
|
@override_settings(
|
|
|
|
SECRET_KEY="secret",
|
|
|
|
SECRET_KEY_FALLBACKS=["oldsecret"],
|
|
|
|
)
|
|
|
|
def test_default_keys_verification(self):
|
|
|
|
old_signer = signing.Signer("oldsecret")
|
|
|
|
signed = old_signer.sign("abc")
|
|
|
|
signer = signing.Signer()
|
|
|
|
self.assertEqual(signer.unsign(signed), "abc")
|
|
|
|
|
2013-11-03 05:34:05 +08:00
|
|
|
|
2015-04-18 05:38:20 +08:00
|
|
|
class TestTimestampSigner(SimpleTestCase):
|
2011-05-21 22:41:14 +08:00
|
|
|
def test_timestamp_signer(self):
|
2012-06-08 00:08:47 +08:00
|
|
|
value = "hello"
|
2014-11-11 02:33:49 +08:00
|
|
|
with freeze_time(123456789):
|
2011-06-17 17:47:08 +08:00
|
|
|
signer = signing.TimestampSigner("predictable-key")
|
|
|
|
ts = signer.sign(value)
|
2016-04-08 10:04:45 +08:00
|
|
|
self.assertNotEqual(ts, signing.Signer("predictable-key").sign(value))
|
2011-06-17 17:47:08 +08:00
|
|
|
self.assertEqual(signer.unsign(ts), value)
|
2014-11-11 02:33:49 +08:00
|
|
|
|
|
|
|
with freeze_time(123456800):
|
2011-06-17 17:47:08 +08:00
|
|
|
self.assertEqual(signer.unsign(ts, max_age=12), value)
|
2014-11-15 18:03:04 +08:00
|
|
|
# max_age parameter can also accept a datetime.timedelta object
|
|
|
|
self.assertEqual(
|
|
|
|
signer.unsign(ts, max_age=datetime.timedelta(seconds=11)), value
|
|
|
|
)
|
2016-01-17 19:26:39 +08:00
|
|
|
with self.assertRaises(signing.SignatureExpired):
|
|
|
|
signer.unsign(ts, max_age=10)
|
2021-05-06 05:28:08 +08:00
|
|
|
|
|
|
|
|
|
|
|
class TestBase62(SimpleTestCase):
|
|
|
|
def test_base62(self):
|
|
|
|
tests = [-(10**10), 10**10, 1620378259, *range(-100, 100)]
|
|
|
|
for i in tests:
|
|
|
|
self.assertEqual(i, signing.b62_decode(signing.b62_encode(i)))
|