Enable generating encrypted JSON payloads with the Diaspora protocol

This adds possibility for private message support.

JSON encrypted payload encryption and decryption is handled by the Diaspora `EncryptedPayload` class.

Refs: #82
merge-requests/130/head
Jason Robinson 2018-02-04 01:09:30 +02:00
rodzic 02d110caa9
commit c1efc1add1
4 zmienionych plików z 49 dodań i 5 usunięć

Wyświetl plik

@ -35,6 +35,10 @@
* Support fetching new style Diaspora protocol Webfinger (RFC 3033) ([related issue](https://github.com/jaywink/federation/issues/108))
The legaxy Webfinger is still used as fallback if the new Webfinger is not found.
* Enable generating encrypted JSON payloads with the Diaspora protocol which adds private message support. ([related issue](https://github.com/jaywink/federation/issues/82))
JSON encrypted payload encryption and decryption is handled by the Diaspora `EncryptedPayload` class.
### Changed
* Refactoring for Diaspora `MagicEnvelope` class.

Wyświetl plik

@ -1,7 +1,8 @@
import json
from base64 import b64decode
from base64 import b64decode, b64encode
from Crypto.Cipher import PKCS1_v1_5, AES
from Crypto.Random import get_random_bytes
from lxml import etree
@ -28,3 +29,33 @@ class EncryptedPayload:
encrypter = AES.new(key, AES.MODE_CBC, iv)
content = encrypter.decrypt(encrypted_magic_envelope)
return etree.fromstring(pkcs7_unpad(content))
@staticmethod
def get_aes_key_json():
iv = get_random_bytes(AES.block_size)
key = get_random_bytes(32)
encrypter = AES.new(key, AES.MODE_CBC, iv)
return {
"key": b64encode(key),
"iv": b64encode(iv),
}, encrypter
@staticmethod
def encrypt(payload, public_key):
"""
Encrypt a payload using an encrypted JSON wrapper.
See: <insert link to docs>
:param payload: Payload document as a string.
:param public_key: Public key of recipient as an RSA object.
:return: Encrypted JSON wrapper as dict.
"""
aes_key_json, encrypter = EncryptedPayload.get_aes_key_json()
encrypted_me = encrypter.encrypt(payload)
cipher = PKCS1_v1_5.new(public_key)
aes_key = cipher.encrypt(aes_key_json)
return {
"aes_key": b64encode(aes_key),
"encrypted_magic_envelope": b64encode(encrypted_me),
}

Wyświetl plik

@ -230,12 +230,21 @@ class Protocol(BaseProtocol):
return data[0:-data[-1]]
def build_send(self, entity, from_user, to_user=None, *args, **kwargs):
"""Build POST data for sending out to remotes."""
"""
Build POST data for sending out to remotes.
:param entity: The outbound ready entity for this protocol.
:param from_user: The user sending this payload. Must have ``private_key`` and ``handle`` properties.
:param to_user: (Optional) user to send private payload to. Must have ``key`` as receiving user public key.
:returns: dict or string depending on if private or public payload.
"""
if entity.outbound_doc is not None:
# Use pregenerated outbound document
xml = entity.outbound_doc
else:
xml = entity.to_xml()
me = MagicEnvelope(etree.tostring(xml), private_key=from_user.private_key, author_handle=from_user.handle)
# TODO wrap if doing encrypted delivery
return me.render()
rendered = me.render()
if to_user:
return EncryptedPayload.encrypt(rendered, to_user.key)
return rendered

Wyświetl plik

@ -80,7 +80,7 @@ def send_document(url, data, timeout=10, *args, **kwargs):
Additional ``*args`` and ``**kwargs`` will be passed on to ``requests.post``.
:arg url: Full url to send to, including protocol
:arg data: POST data to send (dict)
:arg data: Dictionary (will be form-encoded), bytes, or file-like object to send in the body
:arg timeout: Seconds to wait for response (defaults to 10)
:returns: Tuple of status code (int or None) and error (exception class instance or None)
"""