Skip to content
This repository has been archived by the owner on Jul 13, 2023. It is now read-only.

Commit

Permalink
feat: accept aes128gcm content encoding
Browse files Browse the repository at this point in the history
Closes #930
  • Loading branch information
jrconlin committed Jun 22, 2017
1 parent 14f3d87 commit 00947d1
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 6 deletions.
3 changes: 2 additions & 1 deletion autopush/router/fcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,9 @@ def _route(self, notification, router_data):

data['body'] = notification.data
data['con'] = notification.headers['encoding']
data['enc'] = notification.headers['encryption']

if 'encryption' in notification.headers:
data['enc'] = notification.headers['encryption']
if 'crypto_key' in notification.headers:
data['cryptokey'] = notification.headers['crypto_key']
elif 'encryption_key' in notification.headers:
Expand Down
3 changes: 2 additions & 1 deletion autopush/router/gcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ def _route(self, notification, uaid_data):

data['body'] = notification.data
data['con'] = notification.headers['encoding']
data['enc'] = notification.headers['encryption']

if 'encryption' in notification.headers:
data['enc'] = notification.headers.get('encryption')
if 'crypto_key' in notification.headers:
data['cryptokey'] = notification.headers['crypto_key']
elif 'encryption_key' in notification.headers:
Expand Down
94 changes: 94 additions & 0 deletions autopush/tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1865,6 +1865,100 @@ def test_registration(self):
eq_(ca_data['enc'], salt)
eq_(ca_data['body'], base64url_encode(data))

@inlineCallbacks
def test_registration_aes128gcm(self):
self._add_router()
# get the senderid
url = "{}/v1/{}/{}/registration".format(
self.ep.settings.endpoint_url,
"gcm",
self.senderID,
)
response, body = yield _agent('POST', url, body=json.dumps(
{"chid": str(uuid.uuid4()),
"token": uuid.uuid4().hex,
}
))
eq_(response.code, 200)
jbody = json.loads(body)

# Send a fake message
data = ("\xa2\xa5\xbd\xda\x40\xdc\xd1\xa5\xf9\x6a\x60\xa8\x57\x7b\x48"
"\xe4\x43\x02\x5a\x72\xe0\x64\x69\xcd\x29\x6f\x65\x44\x53\x78"
"\xe1\xd9\xf6\x46\x26\xce\x69")
content_encoding = "aes128gcm"

response, body = yield _agent(
'POST',
str(jbody['endpoint']),
headers=Headers({
"ttl": ["0"],
"content-encoding": [content_encoding],
}),
body=data
)

ca_data = self._mock_send.call_args[0][0].data
eq_(response.code, 201)
# ChannelID here MUST match what we got from the registration call.
# Currently, this is a lowercase, hex UUID without dashes.
eq_(ca_data['chid'], jbody['channelID'])
eq_(ca_data['con'], content_encoding)
eq_(ca_data['body'], base64url_encode(data))
ok_('enc' not in ca_data)

@inlineCallbacks
def test_registration_aes128gcm_bad_(self):
self._add_router()
# get the senderid
url = "{}/v1/{}/{}/registration".format(
self.ep.settings.endpoint_url,
"gcm",
self.senderID,
)
response, body = yield _agent('POST', url, body=json.dumps(
{"chid": str(uuid.uuid4()),
"token": uuid.uuid4().hex,
}
))
eq_(response.code, 200)
jbody = json.loads(body)

# Send a fake message
data = ("\xa2\xa5\xbd\xda\x40\xdc\xd1\xa5\xf9\x6a\x60\xa8\x57\x7b\x48"
"\xe4\x43\x02\x5a\x72\xe0\x64\x69\xcd\x29\x6f\x65\x44\x53\x78"
"\xe1\xd9\xf6\x46\x26\xce\x69")
crypto_key = ("keyid=p256dh;dh=BAFJxCIaaWyb4JSkZopERL9MjXBeh3WdBxew"
"SYP0cZWNMJaT7YNaJUiSqBuGUxfRj-9vpTPz5ANmUYq3-u-HWOI")
salt = "keyid=p256dh;salt=S82AseB7pAVBJ2143qtM3A"
content_encoding = "aes128gcm"

response, body = yield _agent(
'POST',
str(jbody['endpoint']),
headers=Headers({
"ttl": ["0"],
"content-encoding": [content_encoding],
"crypto-key": [crypto_key]
}),
body=data
)

eq_(response.code, 400)
ok_("do not include 'dh' in " in body.lower())
response, body = yield _agent(
'POST',
str(jbody['endpoint']),
headers=Headers({
"ttl": ["0"],
"content-encoding": [content_encoding],
"encryption": [salt]
}),
body=data
)
eq_(response.code, 400)
ok_("do not include 'salt' in " in body.lower())

@inlineCallbacks
def test_registration_no_token(self):
self._add_router()
Expand Down
44 changes: 40 additions & 4 deletions autopush/web/webpush.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
validates_schema,
)
from marshmallow_polyfield import PolyField
from marshmallow.validate import OneOf
from marshmallow.validate import Equal
from twisted.logger import Logger # noqa
from twisted.internet.defer import Deferred # noqa
from twisted.internet.defer import maybeDeferred
Expand Down Expand Up @@ -170,7 +170,7 @@ class WebPushCrypto01HeaderSchema(Schema):
content_encoding = fields.String(
required=True,
load_from="content-encoding",
validate=OneOf(["aesgcm128"])
validate=Equal("aesgcm128")
)
encryption = fields.String(required=True)
encryption_key = fields.String(
Expand Down Expand Up @@ -213,13 +213,13 @@ def validate_encryption_key(self, value):
class WebPushCrypto04HeaderSchema(Schema):
"""Validates WebPush Message Encryption
Uses draft-ietf-webpush-encryption-04 rules for validation.
Uses draft-ietf-httpbis-encryption-encoding-04 rules for validation.
"""
content_encoding = fields.String(
required=True,
load_from="content-encoding",
validate=OneOf(["aesgcm"])
validate=Equal("aesgcm")
)
encryption = fields.String(required=True)
crypto_key = fields.String(
Expand Down Expand Up @@ -255,6 +255,40 @@ def reject_encryption_key(self, data, original_data):
)


class WebPushCrypto06HeaderSchema(Schema):
"""Validates WebPush Message Encryption
Uses draft-ietf-httpbis-encryption-encoding-06 rules for validation
"""

content_encoding = fields.String(
required=True,
load_from="content-encoding",
validate=Equal("aes128gcm")
)

encryption = fields.String(required=False)
crypto_key = fields.String(required=False,
load_from="crypto-key")

@validates("encryption")
def validate_encryption(self, value):
if CryptoKey.parse_and_get_label(value, "salt"):
raise InvalidRequest("Do not include 'salt' in aes128gcm "
"Encryption header",
status_code=400,
errno=110)

@validates("crypto_key")
def validate_crypto_key(self, value):
if CryptoKey.parse_and_get_label(value, "dh"):
raise InvalidRequest("Do not include 'dh' in aes128gcm "
"Crypto-Key header",
status_code=400,
errno=110)


class WebPushInvalidContentEncodingSchema(Schema):
"""Returned to raise an Invalid Content-encoding error"""
@validates_schema
Expand All @@ -275,6 +309,8 @@ def conditional_crypto_deserialize(object_dict, parent_object_dict):
return WebPushCrypto01HeaderSchema()
elif encoding == "aesgcm":
return WebPushCrypto04HeaderSchema()
elif encoding == "aes128gcm":
return WebPushCrypto06HeaderSchema()
else:
return WebPushInvalidContentEncodingSchema()
else:
Expand Down

0 comments on commit 00947d1

Please sign in to comment.