kopia lustrzana https://gitlab.com/jaywink/federation
Support receiving Diaspora new style encrypted JSON payloads
Decrypt the JSON and extract the Magic Envelope inside. Closes #83merge-requests/130/head
rodzic
ccf161a5d3
commit
2e8d608256
|
@ -2,6 +2,10 @@
|
|||
|
||||
## [unreleased]
|
||||
|
||||
### Added
|
||||
* New style Diaspora private encrypted JSON payloads are now supported in the receiving side. Outbound private Diaspora payloads are still sent as legacy encrypted payloads. ([issue](https://github.com/jaywink/federation/issues/83))
|
||||
* No additional changes need to be made when calling `handle_receive` from your task processing. Just pass in the full received XML or JSON payload as a string with recipient user object as before.
|
||||
|
||||
### Fixed
|
||||
* Fix getting sender from a combination of legacy Diaspora encrypted payload and new entity names (for example `author`). This combination probably only existed in this library.
|
||||
* Correctly extend entity `_children`. Certain Diaspora payloads caused `_children` for an entity to be written over by an empty list, causing for example status message photos to not be saved. Correctly do an extend on it. ([issue](https://github.com/jaywink/federation/issues/89))
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import json
|
||||
from base64 import b64decode
|
||||
|
||||
from Crypto.Cipher import PKCS1_v1_5, AES
|
||||
from lxml import etree
|
||||
|
||||
|
||||
def pkcs7_unpad(data):
|
||||
"""Remove the padding bytes that were added at point of encryption."""
|
||||
if isinstance(data, str):
|
||||
return data[0:-ord(data[-1])]
|
||||
else:
|
||||
return data[0:-data[-1]]
|
||||
|
||||
|
||||
class EncryptedPayload:
|
||||
"""Diaspora encrypted JSON payloads."""
|
||||
|
||||
@staticmethod
|
||||
def decrypt(payload, private_key):
|
||||
"""Decrypt an encrypted JSON payload and return the Magic Envelope document inside."""
|
||||
cipher = PKCS1_v1_5.new(private_key)
|
||||
aes_key = json.loads(
|
||||
cipher.decrypt(b64decode(payload.get("aes_key")), sentinel=None)
|
||||
)
|
||||
key = b64decode(aes_key.get("key"))
|
||||
iv = b64decode(aes_key.get("iv"))
|
||||
encrypted_magic_envelope = b64decode(payload.get("encrypted_magic_envelope"))
|
||||
encrypter = AES.new(key, AES.MODE_CBC, iv)
|
||||
content = encrypter.decrypt(encrypted_magic_envelope)
|
||||
return etree.fromstring(pkcs7_unpad(content))
|
|
@ -2,6 +2,7 @@ import json
|
|||
import logging
|
||||
import warnings
|
||||
from base64 import b64decode, urlsafe_b64decode, b64encode, urlsafe_b64encode
|
||||
from json import JSONDecodeError
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
from Crypto.Cipher import AES, PKCS1_v1_5
|
||||
|
@ -13,6 +14,7 @@ from lxml import etree
|
|||
|
||||
from federation.exceptions import EncryptedMessageError, NoSenderKeyFoundError, SignatureVerificationError
|
||||
from federation.protocols.base import BaseProtocol
|
||||
from federation.protocols.diaspora.encrypted import EncryptedPayload
|
||||
from federation.protocols.diaspora.magic_envelope import MagicEnvelope
|
||||
|
||||
logger = logging.getLogger("federation")
|
||||
|
@ -55,17 +57,35 @@ class Protocol(BaseProtocol):
|
|||
|
||||
Mostly taken from Pyaspora (https://github.com/lukeross/pyaspora).
|
||||
"""
|
||||
def receive(self, payload, user=None, sender_key_fetcher=None, skip_author_verification=False, *args, **kwargs):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.encrypted = self.legacy = False
|
||||
|
||||
def get_json_payload_magic_envelope(self, payload):
|
||||
"""Encrypted JSON payload"""
|
||||
private_key = self._get_user_key(self.user)
|
||||
return EncryptedPayload.decrypt(payload=payload, private_key=private_key)
|
||||
|
||||
def store_magic_envelope_doc(self, payload):
|
||||
"""Get the Magic Envelope, trying JSON first."""
|
||||
try:
|
||||
json_payload = json.loads(payload)
|
||||
logger.debug("diaspora.protocol.store_magic_envelope_doc: json payload: %s", json_payload)
|
||||
self.doc = self.get_json_payload_magic_envelope(json_payload)
|
||||
except JSONDecodeError:
|
||||
# XML payload
|
||||
xml = unquote_plus(payload)
|
||||
xml = xml.lstrip().encode("utf-8")
|
||||
logger.debug("diaspora.protocol.store_magic_envelope_doc: xml payload: %s", xml)
|
||||
self.doc = etree.fromstring(xml)
|
||||
|
||||
def receive(self, payload, user=None, sender_key_fetcher=None, skip_author_verification=False):
|
||||
"""Receive a payload.
|
||||
|
||||
For testing purposes, `skip_author_verification` can be passed. Authorship will not be verified."""
|
||||
self.user = user
|
||||
self.get_contact_key = sender_key_fetcher
|
||||
# Prepare payload
|
||||
xml = unquote_plus(payload)
|
||||
xml = xml.lstrip().encode("utf-8")
|
||||
logger.debug("diaspora.protocol.receive: xml content: %s", xml)
|
||||
self.doc = etree.fromstring(xml)
|
||||
self.store_magic_envelope_doc(payload)
|
||||
# Check for a legacy header
|
||||
self.find_header()
|
||||
# Open payload and get actual message
|
||||
|
@ -83,7 +103,6 @@ class Protocol(BaseProtocol):
|
|||
return self.user.private_key
|
||||
|
||||
def find_header(self):
|
||||
self.encrypted = self.legacy = False
|
||||
self.header = self.doc.find(".//{"+PROTOCOL_NS+"}header")
|
||||
if self.header != None:
|
||||
# Legacy public header found
|
||||
|
|
Ładowanie…
Reference in New Issue