epicyon/devices.py

199 lines
6.4 KiB
Python
Raw Normal View History

2020-08-05 12:05:39 +00:00
__filename__ = "devices.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
2022-02-03 13:58:20 +00:00
__version__ = "1.3.0"
2020-08-05 12:05:39 +00:00
__maintainer__ = "Bob Mottram"
2021-09-10 16:14:50 +00:00
__email__ = "bob@libreserver.org"
2020-08-05 12:05:39 +00:00
__status__ = "Production"
2021-06-26 11:16:41 +00:00
__module_group__ = "Security"
2020-08-05 12:05:39 +00:00
2020-08-06 16:33:19 +00:00
# REST API overview
#
# To support Olm, the following APIs are required:
#
# * Uploading keys for a device (current app)
# POST /api/v1/crypto/keys/upload
#
# * Querying available devices of people you want to establish a session with
# POST /api/v1/crypto/keys/query
#
# * Claiming a pre-key (one-time-key) for each device you want to establish
# a session with
# POST /api/v1/crypto/keys/claim
#
# * Sending encrypted messages directly to specific devices of other people
# POST /api/v1/crypto/delivery
#
# * Collect encrypted messages addressed to the current device
# GET /api/v1/crypto/encrypted_messages
#
# * Clear all encrypted messages addressed to the current device
# POST /api/v1/crypto/encrypted_messages/clear
2020-08-05 12:05:39 +00:00
import os
2021-12-26 15:13:34 +00:00
from utils import load_json
2021-12-26 14:47:21 +00:00
from utils import save_json
2021-12-26 12:02:29 +00:00
from utils import acct_dir
2021-12-26 10:19:59 +00:00
from utils import local_actor_url
2020-08-05 14:06:04 +00:00
2021-12-29 21:55:09 +00:00
def e2e_eremove_device(base_dir: str, nickname: str, domain: str,
2022-01-02 12:32:48 +00:00
device_id: str) -> bool:
2020-08-05 14:06:04 +00:00
"""Unregisters a device for e2ee
"""
2022-01-02 12:32:48 +00:00
person_dir = acct_dir(base_dir, nickname, domain)
device_filename = person_dir + '/devices/' + device_id + '.json'
if os.path.isfile(device_filename):
try:
2022-01-02 12:32:48 +00:00
os.remove(device_filename)
2021-11-25 18:42:38 +00:00
except OSError:
2022-01-02 12:32:48 +00:00
print('EX: e2e_eremove_device unable to delete ' + device_filename)
2020-08-05 14:06:04 +00:00
return True
return False
2021-12-29 21:55:09 +00:00
def e2e_evalid_device(deviceJson: {}) -> bool:
2020-08-06 20:56:14 +00:00
"""Returns true if the given json contains valid device keys
"""
if not isinstance(deviceJson, dict):
return False
if not deviceJson.get('deviceId'):
return False
if not isinstance(deviceJson['deviceId'], str):
return False
if not deviceJson.get('type'):
return False
if not isinstance(deviceJson['type'], str):
return False
2020-08-06 21:23:17 +00:00
if not deviceJson.get('name'):
return False
if not isinstance(deviceJson['name'], str):
return False
2020-08-06 20:56:14 +00:00
if deviceJson['type'] != 'Device':
return False
if not deviceJson.get('claim'):
return False
if not isinstance(deviceJson['claim'], str):
return False
if not deviceJson.get('fingerprintKey'):
return False
if not isinstance(deviceJson['fingerprintKey'], dict):
return False
if not deviceJson['fingerprintKey'].get('type'):
return False
if not isinstance(deviceJson['fingerprintKey']['type'], str):
return False
if not deviceJson['fingerprintKey'].get('publicKeyBase64'):
return False
if not isinstance(deviceJson['fingerprintKey']['publicKeyBase64'], str):
return False
if not deviceJson.get('identityKey'):
return False
if not isinstance(deviceJson['identityKey'], dict):
return False
if not deviceJson['identityKey'].get('type'):
return False
if not isinstance(deviceJson['identityKey']['type'], str):
return False
if not deviceJson['identityKey'].get('publicKeyBase64'):
return False
if not isinstance(deviceJson['identityKey']['publicKeyBase64'], str):
return False
return True
2021-12-29 21:55:09 +00:00
def e2e_eadd_device(base_dir: str, nickname: str, domain: str,
2022-01-02 12:32:48 +00:00
device_id: str, name: str, claim_url: str,
fingerprint_public_key: str,
identity_public_key: str,
fingerprint_key_type="Ed25519Key",
identity_key_type="Curve25519Key") -> bool:
2020-08-05 14:06:04 +00:00
"""Registers a device for e2ee
2022-01-02 12:32:48 +00:00
claim_url could be something like:
2020-08-05 14:06:04 +00:00
http://localhost:3000/users/admin/claim?id=11119
"""
2022-01-02 12:32:48 +00:00
if ' ' in device_id or '/' in device_id or \
'?' in device_id or '#' in device_id or \
'.' in device_id:
2020-08-05 14:06:04 +00:00
return False
2022-01-02 12:32:48 +00:00
person_dir = acct_dir(base_dir, nickname, domain)
if not os.path.isdir(person_dir):
2020-08-05 14:06:04 +00:00
return False
2022-01-02 12:32:48 +00:00
if not os.path.isdir(person_dir + '/devices'):
os.mkdir(person_dir + '/devices')
device_dict = {
"deviceId": device_id,
2020-08-05 14:06:04 +00:00
"type": "Device",
"name": name,
2022-01-02 12:32:48 +00:00
"claim": claim_url,
2020-08-05 14:06:04 +00:00
"fingerprintKey": {
2022-01-02 12:32:48 +00:00
"type": fingerprint_key_type,
"publicKeyBase64": fingerprint_public_key
2020-08-05 14:06:04 +00:00
},
"identityKey": {
2022-01-02 12:32:48 +00:00
"type": identity_key_type,
"publicKeyBase64": identity_public_key
2020-08-05 14:06:04 +00:00
}
}
2022-01-02 12:32:48 +00:00
device_filename = person_dir + '/devices/' + device_id + '.json'
return save_json(device_dict, device_filename)
2020-08-05 12:05:39 +00:00
2021-12-29 21:55:09 +00:00
def e2e_edevices_collection(base_dir: str, nickname: str, domain: str,
domain_full: str, http_prefix: str) -> {}:
2020-08-05 12:05:39 +00:00
"""Returns a list of registered devices
"""
2022-01-02 12:32:48 +00:00
person_dir = acct_dir(base_dir, nickname, domain)
if not os.path.isdir(person_dir):
2020-08-05 12:05:39 +00:00
return {}
2022-01-02 12:32:48 +00:00
person_id = local_actor_url(http_prefix, nickname, domain_full)
if not os.path.isdir(person_dir + '/devices'):
os.mkdir(person_dir + '/devices')
device_list = []
for _, _, files in os.walk(person_dir + '/devices/'):
2020-08-05 12:05:39 +00:00
for dev in files:
if not dev.endswith('.json'):
continue
2022-01-02 12:32:48 +00:00
device_filename = os.path.join(person_dir + '/devices', dev)
dev_json = load_json(device_filename)
if dev_json:
device_list.append(dev_json)
2020-12-13 22:13:45 +00:00
break
2020-08-05 12:05:39 +00:00
2022-01-02 12:32:48 +00:00
devices_dict = {
'id': person_id + '/collections/devices',
2020-08-05 12:05:39 +00:00
'type': 'Collection',
2022-01-02 12:32:48 +00:00
'totalItems': len(device_list),
'items': device_list
2020-08-05 12:05:39 +00:00
}
2022-01-02 12:32:48 +00:00
return devices_dict
2020-08-05 12:47:15 +00:00
2021-12-29 21:55:09 +00:00
def e2e_edecrypt_message_from_device(message_json: {}) -> str:
2020-08-05 12:47:15 +00:00
"""Locally decrypts a message on the device.
This should probably be a link to a local script
or native app, such that what the user sees isn't
something which the server could get access to.
"""
# TODO
# {
# "type": "EncryptedMessage",
# "messageType": 0,
# "cipherText": "...",
# "digest": {
# "type": "Digest",
# "digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#hmac-sha256",
# "digestValue": "5f6ad31acd64995483d75c7..."
# },
# "messageFranking": "...",
# "attributedTo": {
# "type": "Device",
# "deviceId": "11119"
# },
# "to": {
# "type": "Device",
# "deviceId": "11876"
# }
# }
return ''