Skip to content

Commit af51cb1

Browse files
committed
fix hmac.compare_digest under python277
fix indent implement tscmp for preventing timing attack remove semicolon apply flake8 apply flake8 refactoring using hasattr refactoring using len2 directly add test and refactoring
1 parent a9b3f6e commit af51cb1

File tree

3 files changed

+58
-4
lines changed

3 files changed

+58
-4
lines changed

linebot/utils.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,26 @@ def to_camel_case(text):
4545
"""
4646
split = text.split('_')
4747
return split[0] + "".join(x.title() for x in split[1:])
48+
49+
50+
def safe_compare_digest(val1, val2):
51+
"""compare_digest method. for test extract safe_compare_digest method
52+
53+
:param str or bytes val1: string or bytes for compare
54+
:param str or bytes val2: string or bytes for compare
55+
:rtype: bool
56+
:return: result
57+
"""
58+
59+
if len(val1) != len(val2):
60+
return False
61+
62+
result = 0
63+
if PY3 and isinstance(val1, bytes) and isinstance(val2, bytes):
64+
for i, j in zip(val1, val2):
65+
result |= i ^ j
66+
else:
67+
for i, j in zip(val1, val2):
68+
result |= (ord(i) ^ ord(j))
69+
70+
return result == 0

linebot/webhook.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,29 @@
3232
PostbackEvent,
3333
BeaconEvent
3434
)
35-
from .utils import LOGGER, PY3
35+
from .utils import LOGGER, PY3, safe_compare_digest
36+
37+
38+
if hasattr(hmac, "compare_digest"):
39+
def compare_digest(val1, val2):
40+
"""compare_digest method.
41+
42+
:param str or bytes val1: string or bytes for compare
43+
:param str or bytes val2: string or bytes for compare
44+
:rtype: bool
45+
:return: result
46+
"""
47+
return hmac.compare_digest(val1, val2)
48+
else:
49+
def compare_digest(val1, val2):
50+
"""compare_digest method.
51+
52+
:param str or bytes val1: string or bytes for compare
53+
:param str or bytes val2: string or bytes for compare
54+
:rtype: bool
55+
:return: result
56+
"""
57+
return safe_compare_digest(val1, val2)
3658

3759

3860
class SignatureValidator(object):
@@ -64,8 +86,8 @@ def validate(self, body, signature):
6486
hashlib.sha256
6587
).digest()
6688

67-
return hmac.compare_digest(
68-
signature.encode('utf-8'), base64.b64encode(gen_signature)
89+
return compare_digest(
90+
signature.encode('utf-8'), base64.b64encode(gen_signature)
6991
)
7092

7193

tests/test_utils.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import unittest
1818

19-
from linebot.utils import to_camel_case, to_snake_case
19+
from linebot.utils import to_camel_case, to_snake_case, safe_compare_digest
2020

2121

2222
class TestUtils(unittest.TestCase):
@@ -26,6 +26,15 @@ def test_to_snake_case(self):
2626
def test_to_camel_case(self):
2727
self.assertEqual(to_camel_case('hoge_bar'), 'hogeBar')
2828

29+
def test_safe_compare_digest_true(self):
30+
self.assertTrue(safe_compare_digest('/gg9a+LvFevTH1sd7', '/gg9a+LvFevTH1sd7'))
31+
32+
def test_safe_compare_digest_false_same_size(self):
33+
self.assertFalse(safe_compare_digest('/gg9a+LvFevTH1sd7', '/gg9a+LvFevTH1sd8'))
34+
35+
def test_safe_compare_digest_false_different_size(self):
36+
self.assertFalse(safe_compare_digest('/gg9a+LvFevTH1sd7', '/gg9a+LvFevTH1sd78'))
37+
2938

3039
if __name__ == '__main__':
3140
unittest.main()

0 commit comments

Comments
 (0)