Skip to content

Commit

Permalink
AP signature verification: just log for now, don't fail
Browse files Browse the repository at this point in the history
for #315
  • Loading branch information
snarfed committed Feb 15, 2023
1 parent 63d0e59 commit e5d3f94
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 11 deletions.
13 changes: 8 additions & 5 deletions activitypub.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def inbox(domain=None):
activity = request.json
assert activity
except (TypeError, ValueError, AssertionError):
error(f"Couldn't parse body as JSON", exc_info=True)
error("Couldn't parse body as JSON", exc_info=True)

type = activity.get('type')
actor = activity.get('actor')
Expand Down Expand Up @@ -154,21 +154,24 @@ def inbox(domain=None):
if request.headers.get('Signature'):
digest = request.headers.get('Digest')
if not digest:
error(f'Missing Digest header, required for HTTP Signature', status=401)
logger.warning('Missing Digest header, required for HTTP Signature')

expected = b64encode(sha256(request.data).digest()).decode()
if digest.removeprefix('SHA-256=') != expected:
error(f'Invalid Digest header, required for HTTP Signature', status=401)
logger.warning('Invalid Digest header, required for HTTP Signature')

# TODO: check keyId
key = actor.get('publicKey', {}).get('publicKeyPem', {})
try:
if not HeaderVerifier(request.headers, key, method='GET', path=request.path,
required_headers=common.HTTP_SIG_HEADERS,
sign_header='signature').verify():
error(f'HTTP Signature verification failed', status=401)
logger.warning('HTTP Signature verification failed')
except BaseException as e:
error(f'HTTP Signature verification failed: {e}', status=401)
logger.warning(f'HTTP Signature verification failed: {e}')
logger.info('HTTP Signature verified!')
else:
logger.info('No HTTP Signature')

# handle activity!
if type == 'Undo' and obj_as2.get('type') == 'Follow':
Expand Down
38 changes: 32 additions & 6 deletions tests/test_activitypub.py
Original file line number Diff line number Diff line change
Expand Up @@ -726,22 +726,48 @@ def test_inbox_verify_http_signature(self, _, mock_get, ___):
headers = hs.sign(headers, method='GET', path='/inbox')

# valid signature
resp = self.client.post('/inbox', data=body, headers=headers)
self.assertEqual(200, resp.status_code)
with self.assertLogs(activitypub.logger, level='INFO') as logs:
resp = self.client.post('/inbox', data=body, headers=headers)
self.assertEqual(200, resp.status_code, resp.get_data(as_text=True))
self.assertIn('INFO:activitypub:HTTP Signature verified!', logs.output)

# invalid signature, content changed
activitypub.seen_ids.clear()
obj_key = ndb.Key(Object, NOTE['id'])
obj_key.delete()
resp = self.client.post('/inbox', json={**NOTE, 'content': 'z'}, headers=headers)
self.assertEqual(401, resp.status_code)

with self.assertLogs(activitypub.logger, level='WARNING') as logs:
resp = self.client.post('/inbox', json={**NOTE, 'content': 'z'},
headers=headers)
self.assertEqual(200, resp.status_code, resp.get_data(as_text=True))
self.assertIn(
'WARNING:activitypub:Invalid Digest header, required for HTTP Signature',
logs.output)

# TODO once we start failing on bad sigs
# self.assertEqual(401, resp.status_code)

# invalid signature, header changed
activitypub.seen_ids.clear()
obj_key.delete()
orig_date = headers['Date']
resp = self.client.post('/inbox', data=body, headers={**headers, 'Date': 'X'})
self.assertEqual(401, resp.status_code)

with self.assertLogs(activitypub.logger, level='WARNING') as logs:
resp = self.client.post('/inbox', data=body, headers={**headers, 'Date': 'X'})
self.assertEqual(200, resp.status_code, resp.get_data(as_text=True))
self.assertIn('WARNING:activitypub:HTTP Signature verification failed',
logs.output)

# TODO once we start failing on bad sigs
# self.assertEqual(401, resp.status_code)

# no signature
activitypub.seen_ids.clear()
obj_key.delete()
with self.assertLogs(activitypub.logger, level='INFO') as logs:
resp = self.client.post('/inbox', json=NOTE)
self.assertEqual(200, resp.status_code, resp.get_data(as_text=True))
self.assertIn('INFO:activitypub:No HTTP Signature', logs.output)

def test_delete_actor(self, _, mock_get, ___):
follower = Follower.get_or_create('foo.com', DELETE['actor'])
Expand Down

0 comments on commit e5d3f94

Please sign in to comment.