Skip to content

Commit f3aabe1

Browse files
committed
Fixup aes128gcm key injection
1 parent 24d746b commit f3aabe1

File tree

2 files changed

+56
-39
lines changed

2 files changed

+56
-39
lines changed

pywebpush/__init__.py

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -181,33 +181,42 @@ def encode(self, data, content_encoding="aes128gcm"):
181181
raise WebPushException("Invalid content encoding specified. "
182182
"Select from " +
183183
json.dumps(self.valid_encodings))
184-
if (content_encoding == "aesgcm"):
184+
if content_encoding == "aesgcm":
185185
salt = os.urandom(16)
186186
# The server key is an ephemeral ECDH key used only for this
187187
# transaction
188188
server_key = ec.generate_private_key(ec.SECP256R1, default_backend())
189-
crypto_key = base64.urlsafe_b64encode(
190-
server_key.public_key().public_numbers().encode_point()
191-
).strip(b'=')
189+
crypto_key = server_key.public_key().public_numbers().encode_point()
192190

193191
if isinstance(data, six.string_types):
194192
data = bytes(data.encode('utf8'))
195-
196-
encrypted = http_ece.encrypt(
197-
data,
198-
salt=salt,
199-
keyid=crypto_key.decode(),
200-
private_key=server_key,
201-
dh=self.receiver_key,
202-
auth_secret=self.auth_key,
203-
version=content_encoding)
204-
205-
reply = CaseInsensitiveDict({
206-
'crypto_key': crypto_key,
207-
'body': encrypted,
208-
})
209-
if salt:
210-
reply['salt'] = base64.urlsafe_b64encode(salt).strip(b'=')
193+
if content_encoding == "aes128gcm":
194+
encrypted = http_ece.encrypt(
195+
data,
196+
salt=salt,
197+
private_key=server_key,
198+
dh=self.receiver_key,
199+
auth_secret=self.auth_key,
200+
version=content_encoding)
201+
reply = CaseInsensitiveDict({
202+
'body': encrypted
203+
})
204+
else:
205+
crypto_key = base64.urlsafe_b64encode(crypto_key).strip(b'=')
206+
encrypted = http_ece.encrypt(
207+
data,
208+
salt=salt,
209+
private_key=server_key,
210+
keyid=crypto_key.decode(),
211+
dh=self.receiver_key,
212+
auth_secret=self.auth_key,
213+
version=content_encoding)
214+
reply = CaseInsensitiveDict({
215+
'crypto_key': crypto_key,
216+
'body': encrypted,
217+
})
218+
if salt:
219+
reply['salt'] = base64.urlsafe_b64encode(salt).strip(b'=')
211220
return reply
212221

213222
def as_curl(self, endpoint, encoded_data, headers):
@@ -236,7 +245,7 @@ def as_curl(self, endpoint, encoded_data, headers):
236245
data = "--data-binary @encrypted.data"
237246
if 'content-length' not in headers:
238247
header_list.append(
239-
'-H "content-length: {}" \\ \n'.format(len(data)))
248+
'-H "content-length: {}" \\ \n'.format(len(encoded_data)))
240249
return ("""curl -vX POST {url} \\\n{headers}{data}""".format(
241250
url=endpoint, headers="".join(header_list), data=data))
242251

@@ -272,23 +281,28 @@ def send(self, data=None, headers=None, ttl=0, gcm_key=None, reg_id=None,
272281
encoded = {}
273282
headers = CaseInsensitiveDict(headers)
274283
if data:
275-
encoded = self.encode(data)
276-
# Append the p256dh to the end of any existing crypto-key
277-
crypto_key = headers.get("crypto-key", "")
278-
if crypto_key:
279-
# due to some confusion by a push service provider, we should
280-
# use ';' instead of ',' to append the headers.
281-
# see https://github.com/webpush-wg/webpush-encryption/issues/6
282-
crypto_key += ';'
283-
crypto_key += ("dh=" + encoded["crypto_key"].decode('utf8'))
284-
headers.update({
285-
'crypto-key': crypto_key,
286-
'content-encoding': content_encoding,
287-
})
288-
if encoded.get('salt'):
284+
encoded = self.encode(data, content_encoding)
285+
if "crypto_key" in encoded:
286+
# Append the p256dh to the end of any existing crypto-key
287+
crypto_key = headers.get("crypto-key", "")
288+
if crypto_key:
289+
# due to some confusion by a push service provider, we
290+
# should use ';' instead of ',' to append the headers.
291+
# see
292+
# https://github.com/webpush-wg/webpush-encryption/issues/6
293+
crypto_key += ';'
294+
crypto_key += (
295+
"dh=" + encoded["crypto_key"].decode('utf8'))
296+
headers.update({
297+
'crypto-key': crypto_key
298+
})
299+
if "salt" in encoded:
289300
headers.update({
290301
'encryption': "salt=" + encoded['salt'].decode('utf8')
291302
})
303+
headers.update({
304+
'content-encoding': content_encoding,
305+
})
292306
if gcm_key:
293307
endpoint = 'https://android.googleapis.com/gcm/send'
294308
reg_ids = []

pywebpush/tests/test_webpush.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,10 @@ def test_encode(self):
100100
if 'salt' in encoded:
101101
raw_salt = base64.urlsafe_b64decode(
102102
push._repad(encoded['salt']))
103-
raw_dh = base64.urlsafe_b64decode(
104-
push._repad(encoded['crypto_key']))
103+
raw_dh = None
104+
if content_encoding != "aes128gcm":
105+
raw_dh = base64.urlsafe_b64decode(
106+
push._repad(encoded['crypto_key']))
105107
raw_auth = base64.urlsafe_b64decode(
106108
push._repad(subscription_info['keys']['auth']))
107109

@@ -148,7 +150,8 @@ def test_send_vapid(self, mock_post):
148150
subscription_info=subscription_info,
149151
data=data,
150152
vapid_private_key=self.vapid_key,
151-
vapid_claims={"sub": "mailto:[email protected]"}
153+
vapid_claims={"sub": "mailto:[email protected]"},
154+
content_encoding="aesgcm"
152155
)
153156
eq_(subscription_info.get('endpoint'), mock_post.call_args[0][0])
154157
pheaders = mock_post.call_args[1].get('headers')
@@ -167,7 +170,7 @@ def repad(str):
167170
ckey = pheaders.get('crypto-key')
168171
ok_('p256ecdsa=' in ckey)
169172
ok_('dh=' in ckey)
170-
eq_(pheaders.get('content-encoding'), 'aes128gcm')
173+
eq_(pheaders.get('content-encoding'), 'aesgcm')
171174

172175
@patch.object(WebPusher, "send")
173176
@patch.object(py_vapid.Vapid, "sign")

0 commit comments

Comments
 (0)