Skip to content

Commit 29759f7

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 add test and refactoring change docstring
1 parent a9b3f6e commit 29759f7

File tree

3 files changed

+61
-4
lines changed

3 files changed

+61
-4
lines changed

linebot/utils.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,25 @@ 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+
"""safe_compare_digest method.
52+
53+
:param val1: string or bytes for compare
54+
:type val1: str | bytes
55+
:param val2: string or bytes for compare
56+
:type val2: str | bytes
57+
"""
58+
if len(val1) != len(val2):
59+
return False
60+
61+
result = 0
62+
if PY3 and isinstance(val1, bytes) and isinstance(val2, bytes):
63+
for i, j in zip(val1, val2):
64+
result |= i ^ j
65+
else:
66+
for i, j in zip(val1, val2):
67+
result |= (ord(i) ^ ord(j))
68+
69+
return result == 0

linebot/webhook.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,33 @@
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 val1: string or bytes for compare
43+
:type val1: str | bytes
44+
:param val2: string or bytes for compare
45+
:type val2: str | bytes
46+
:rtype: bool
47+
:return: result
48+
"""
49+
return hmac.compare_digest(val1, val2)
50+
else:
51+
def compare_digest(val1, val2):
52+
"""compare_digest method.
53+
54+
:param val1: string or bytes for compare
55+
:type val1: str | bytes
56+
:param val2: string or bytes for compare
57+
:type val2: str | bytes
58+
:rtype: bool
59+
:return: result
60+
"""
61+
return safe_compare_digest(val1, val2)
3662

3763

3864
class SignatureValidator(object):
@@ -64,8 +90,8 @@ def validate(self, body, signature):
6490
hashlib.sha256
6591
).digest()
6692

67-
return hmac.compare_digest(
68-
signature.encode('utf-8'), base64.b64encode(gen_signature)
93+
return compare_digest(
94+
signature.encode('utf-8'), base64.b64encode(gen_signature)
6995
)
7096

7197

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)