From 148781c2fda8d45cdd8b72310716025ec7fd4b07 Mon Sep 17 00:00:00 2001 From: JR Conlin Date: Thu, 7 May 2020 08:46:50 -0700 Subject: [PATCH] bug: do not require `encryption` header for APNs `aes128gcm` encoded messages (#1385) * bug: do not require `encryption` header for APNs `aes128gcm` encoded messages Includes better logging for uncaught APNs errors, since those were getting dropped by cyclone. Issue #1383 Closes #1384 --- Dockerfile | 2 +- autopush/base.py | 23 +++++---- autopush/router/apnsrouter.py | 3 +- autopush/tests/test_integration.py | 76 ++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index ea3727e8..7c6e86f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ ENV PATH=$PATH:/root/.cargo/bin RUN \ apt-get update && \ - apt-get install -y -qq libexpat1-dev gcc libssl-dev libffi-dev libjemalloc1 && \ + apt-get install -y -qq libexpat1-dev gcc libssl-dev libffi-dev libjemalloc2 && \ make clean && \ pip install -r requirements.txt && \ pypy setup.py develop diff --git a/autopush/base.py b/autopush/base.py index 11761999..574ef959 100644 --- a/autopush/base.py +++ b/autopush/base.py @@ -57,16 +57,21 @@ def write_error(self, code, **kwargs): websocket. """ - self.set_status(code) - if 'exc_info' in kwargs: + try: + self.set_status(code) + if 'exc_info' in kwargs: + self.log.failure( + format=kwargs.get('format', "Exception"), + failure=failure.Failure(*kwargs['exc_info']), + client_info=self._client_info) + else: + self.log.failure("Error in handler: %s" % code, + client_info=self._client_info) + self.finish() + except Exception as ex: self.log.failure( - format=kwargs.get('format', "Exception"), - failure=failure.Failure(*kwargs['exc_info']), - client_info=self._client_info) - else: - self.log.failure("Error in handler: %s" % code, - client_info=self._client_info) - self.finish() + "error in write_error: {}:{} while printing {};{}".format( + code, ex, kwargs, self._client_info)) def authenticate_peer_cert(self): """Authenticate the client per the configured client_certs. diff --git a/autopush/router/apnsrouter.py b/autopush/router/apnsrouter.py index 590e12a8..eb04575c 100644 --- a/autopush/router/apnsrouter.py +++ b/autopush/router/apnsrouter.py @@ -130,8 +130,9 @@ def _route(self, notification, router_data): if notification.data: payload["body"] = notification.data payload["con"] = notification.headers["encoding"] - payload["enc"] = notification.headers["encryption"] + if "encryption" in notification.headers: + payload["enc"] = notification.headers["encryption"] if "crypto_key" in notification.headers: payload["cryptokey"] = notification.headers["crypto_key"] elif "encryption_key" in notification.headers: diff --git a/autopush/tests/test_integration.py b/autopush/tests/test_integration.py index 0b14e2c2..2abc3d55 100644 --- a/autopush/tests/test_integration.py +++ b/autopush/tests/test_integration.py @@ -2363,6 +2363,82 @@ def test_registration(self): "SentTab.NoTabArrivingNotification.title" assert ca_data['body'] == base64url_encode(data) + @inlineCallbacks + def test_apns_aesgcm_registration_bad(self): + self._add_router() + # get the senderid + url = "{}/v1/{}/{}/registration".format( + self.ep.conf.endpoint_url, + "apns", + "firefox", + ) + response, body = yield _agent('POST', url, body=json.dumps( + {"token": uuid.uuid4().hex} + )) + assert 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") + content_encoding = "aesgcm" + + response, body = yield _agent( + 'POST', + str(jbody['endpoint']), + headers=Headers({ + "crypto-key": [crypto_key], + "ttl": ["0"], + "content-encoding": [content_encoding], + }), + body=data + ) + assert response.code == 400 + + @inlineCallbacks + def test_apns_registration_aes128gcm(self): + self._add_router() + # get the senderid + url = "{}/v1/{}/{}/registration".format( + self.ep.conf.endpoint_url, + "apns", + "firefox", + ) + response, body = yield _agent('POST', url, body=json.dumps( + {"token": uuid.uuid4().hex} + )) + assert 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 = json.loads( + self._mock_connection.request.call_args[1]['body']) + assert response.code == 201 + # ChannelID here MUST match what we got from the registration call. + # Currently, this is a lowercase, hex UUID without dashes. + assert ca_data['chid'] == jbody['channelID'] + assert ca_data['con'] == content_encoding + assert ca_data['body'] == base64url_encode(data) + assert 'enc' not in ca_data + @inlineCallbacks def test_registration_no_token(self): self._add_router()