kopia lustrzana https://gitlab.com/jaywink/federation
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: #82merge-requests/130/head
rodzic
02d110caa9
commit
c1efc1add1
|
@ -35,6 +35,10 @@
|
||||||
* Support fetching new style Diaspora protocol Webfinger (RFC 3033) ([related issue](https://github.com/jaywink/federation/issues/108))
|
* 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.
|
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
|
### Changed
|
||||||
* Refactoring for Diaspora `MagicEnvelope` class.
|
* Refactoring for Diaspora `MagicEnvelope` class.
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import json
|
import json
|
||||||
from base64 import b64decode
|
from base64 import b64decode, b64encode
|
||||||
|
|
||||||
from Crypto.Cipher import PKCS1_v1_5, AES
|
from Crypto.Cipher import PKCS1_v1_5, AES
|
||||||
|
from Crypto.Random import get_random_bytes
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,3 +29,33 @@ class EncryptedPayload:
|
||||||
encrypter = AES.new(key, AES.MODE_CBC, iv)
|
encrypter = AES.new(key, AES.MODE_CBC, iv)
|
||||||
content = encrypter.decrypt(encrypted_magic_envelope)
|
content = encrypter.decrypt(encrypted_magic_envelope)
|
||||||
return etree.fromstring(pkcs7_unpad(content))
|
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),
|
||||||
|
}
|
||||||
|
|
|
@ -230,12 +230,21 @@ class Protocol(BaseProtocol):
|
||||||
return data[0:-data[-1]]
|
return data[0:-data[-1]]
|
||||||
|
|
||||||
def build_send(self, entity, from_user, to_user=None, *args, **kwargs):
|
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:
|
if entity.outbound_doc is not None:
|
||||||
# Use pregenerated outbound document
|
# Use pregenerated outbound document
|
||||||
xml = entity.outbound_doc
|
xml = entity.outbound_doc
|
||||||
else:
|
else:
|
||||||
xml = entity.to_xml()
|
xml = entity.to_xml()
|
||||||
me = MagicEnvelope(etree.tostring(xml), private_key=from_user.private_key, author_handle=from_user.handle)
|
me = MagicEnvelope(etree.tostring(xml), private_key=from_user.private_key, author_handle=from_user.handle)
|
||||||
# TODO wrap if doing encrypted delivery
|
rendered = me.render()
|
||||||
return me.render()
|
if to_user:
|
||||||
|
return EncryptedPayload.encrypt(rendered, to_user.key)
|
||||||
|
return rendered
|
||||||
|
|
|
@ -80,7 +80,7 @@ def send_document(url, data, timeout=10, *args, **kwargs):
|
||||||
Additional ``*args`` and ``**kwargs`` will be passed on to ``requests.post``.
|
Additional ``*args`` and ``**kwargs`` will be passed on to ``requests.post``.
|
||||||
|
|
||||||
:arg url: Full url to send to, including protocol
|
: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)
|
: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)
|
:returns: Tuple of status code (int or None) and error (exception class instance or None)
|
||||||
"""
|
"""
|
||||||
|
|
Ładowanie…
Reference in New Issue