Skip to content

Commit 640d77d

Browse files
authored
Merge pull request #28 from web-push-libs/bug/27
bug: python3 byte string decoding was causing invalid headers
2 parents 9e155d8 + c8ae3d8 commit 640d77d

File tree

2 files changed

+25
-17
lines changed

2 files changed

+25
-17
lines changed

python/py_vapid/__init__.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import os
66
import logging
7+
import binascii
78
import base64
89
import time
910
import hashlib
@@ -126,7 +127,8 @@ def validate(self, validation_token):
126127
:rtype: str
127128
128129
"""
129-
sig = self.private_key.sign(validation_token, hashfunc=self._hasher)
130+
sig = self.private_key.sign(validation_token,
131+
hashfunc=self._hasher)
130132
verification_token = base64.urlsafe_b64encode(sig)
131133
return verification_token
132134

@@ -154,6 +156,11 @@ def _base_sign(self, claims):
154156
"'sub' is your admin email as a mailto: link.")
155157
return claims
156158

159+
def encode(self, bstring):
160+
return binascii.b2a_base64(
161+
bstring).decode('utf8').replace('\n', '').replace(
162+
'+', '-').replace('/', '_').replace('=', '')
163+
157164
def sign(self, claims, crypto_key=None):
158165
"""Sign a set of claims.
159166
:param claims: JSON object containing the JWT claims to use.
@@ -169,7 +176,7 @@ def sign(self, claims, crypto_key=None):
169176
claims = self._base_sign(claims)
170177
sig = jws.sign(claims, self.private_key, algorithm="ES256")
171178
pkey = 'p256ecdsa='
172-
pkey += base64.urlsafe_b64encode(self.public_key.to_string())
179+
pkey += self.encode(self.public_key.to_string())
173180
if crypto_key:
174181
crypto_key = crypto_key + ',' + pkey
175182
else:
@@ -193,12 +200,12 @@ def sign(self, claims, crypto_key=None):
193200
pkey = self.public_key.to_string()
194201
# Make sure that the key is properly prefixed.
195202
if len(pkey) == 64:
196-
pkey = '\04' + pkey
203+
pkey = b'\04' + pkey
197204
return{
198205
"Authorization": "{schema} t={t},k={k}".format(
199206
schema=self._schema,
200207
t=sig,
201-
k=base64.urlsafe_b64encode(pkey).strip('=')
208+
k=self.encode(pkey)
202209
)
203210
}
204211

python/py_vapid/tests/test_vapid.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import binascii
12
import base64
23
import hashlib
34
import os
@@ -24,7 +25,7 @@
2425
"""
2526

2627
T_PUBLIC_RAW = """EJwJZq_GN8jJbo1GGpyU70hmP2hbWAUpQFKDBy\
27-
KB81yldJ9GTklBM5xqEwuPM7VuQcyiLDhvovthPIXx-gsQRQ=="""
28+
KB81yldJ9GTklBM5xqEwuPM7VuQcyiLDhvovthPIXx-gsQRQ==""".strip('=')
2829

2930

3031
def setUp(self):
@@ -44,21 +45,21 @@ def tearDown(self):
4445
class VapidTestCase(unittest.TestCase):
4546
def test_init(self):
4647
v1 = Vapid01(private_key_file="/tmp/private")
47-
eq_(v1.private_key.to_pem(), T_PRIVATE)
48-
eq_(v1.public_key.to_pem(), T_PUBLIC)
48+
eq_(v1.private_key.to_pem(), T_PRIVATE.encode('utf8'))
49+
eq_(v1.public_key.to_pem(), T_PUBLIC.encode('utf8'))
4950
v2 = Vapid01(private_key=T_PRIVATE)
50-
eq_(v2.private_key.to_pem(), T_PRIVATE)
51-
eq_(v2.public_key.to_pem(), T_PUBLIC)
51+
eq_(v2.private_key.to_pem(), T_PRIVATE.encode('utf8'))
52+
eq_(v2.public_key.to_pem(), T_PUBLIC.encode('utf8'))
5253
v3 = Vapid01(private_key=T_DER)
53-
eq_(v3.private_key.to_pem(), T_PRIVATE)
54-
eq_(v3.public_key.to_pem(), T_PUBLIC)
54+
eq_(v3.private_key.to_pem(), T_PRIVATE.encode('utf8'))
55+
eq_(v3.public_key.to_pem(), T_PUBLIC.encode('utf8'))
5556
no_exist = '/tmp/not_exist'
5657
Vapid01(private_key_file=no_exist)
5758
ok_(os.path.isfile(no_exist))
5859
os.unlink(no_exist)
5960

6061
def repad(self, data):
61-
return data + b"===="[:len(data) % 4]
62+
return data + "===="[:len(data) % 4]
6263

6364
@patch("ecdsa.SigningKey.from_pem", side_effect=Exception)
6465
def test_init_bad_priv(self, mm):
@@ -94,7 +95,7 @@ def test_save_public_key(self):
9495

9596
def test_validate(self):
9697
v = Vapid01("/tmp/private")
97-
msg = "foobar"
98+
msg = "foobar".encode('utf8')
9899
vtoken = v.validate(msg)
99100
ok_(v.public_key.verify(base64.urlsafe_b64decode(vtoken),
100101
msg,
@@ -112,7 +113,7 @@ def test_sign_01(self):
112113
items = jws.verify(result['Authorization'].split(' ')[1],
113114
v.public_key,
114115
algorithms=["ES256"])
115-
eq_(json.loads(items), claims)
116+
eq_(json.loads(items.decode('utf8')), claims)
116117
result = v.sign(claims)
117118
eq_(result['Crypto-Key'],
118119
'p256ecdsa=' + T_PUBLIC_RAW)
@@ -131,9 +132,9 @@ def test_sign_02(self):
131132
eq_(len(parts), 2)
132133
t_val = json.loads(base64.urlsafe_b64decode(
133134
self.repad(parts[0][2:].split('.')[1])
134-
))
135-
k_val = base64.urlsafe_b64decode(self.repad(parts[1][2:]))
136-
eq_(k_val[0], "\04")
135+
).decode('utf8'))
136+
k_val = binascii.a2b_base64(self.repad(parts[1][2:]))
137+
eq_(binascii.hexlify(k_val)[:2], b'04')
137138
eq_(len(k_val), 65)
138139
for k in claims:
139140
eq_(t_val[k], claims[k])

0 commit comments

Comments
 (0)