Skip to content

Commit 32cd8ae

Browse files
authored
feat/crypto (#11)
1 parent 3db36c3 commit 32cd8ae

File tree

1 file changed

+40
-7
lines changed

1 file changed

+40
-7
lines changed

ovos_bus_client/message.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@
2525
import re
2626
from copy import deepcopy
2727
from typing import Optional
28-
from ovos_utils.log import LOG
28+
from binascii import hexlify, unhexlify
2929
from ovos_utils.gui import _GUIDict
30+
from ovos_utils.log import LOG
31+
from ovos_utils.security import encrypt, decrypt
32+
from ovos_config.config import Configuration
3033

3134
try:
3235
from lingua_franca.parse import normalize
@@ -35,7 +38,6 @@
3538
def normalize(text, *args, **kwargs):
3639
return text
3740

38-
3941
try:
4042
from mycroft_bus_client.message import Message as _MsgBase, \
4143
CollectionMessage as _CollectionMsgBase
@@ -49,6 +51,7 @@ def normalize(text, *args, **kwargs):
4951
class _MsgBase:
5052
pass
5153

54+
5255
class _CollectionMsgBase(_MsgBase):
5356
pass
5457

@@ -77,6 +80,10 @@ class Message(_MsgBase, metaclass=_MessageMeta):
7780
context: info about the message not part of data such as source,
7881
destination or domain.
7982
"""
83+
# if set all messages are AES encrypted
84+
_secret_key = Configuration().get("websocket", {}).get("secret_key")
85+
# if set to False, will refuse to deserialize unencrypted messages for processing
86+
_allow_unencrypted = Configuration().get("websocket", {}).get("allow_unencrypted", _secret_key is None)
8087

8188
def __init__(self, msg_type, data=None, context=None):
8289
"""Used to construct a message object
@@ -116,15 +123,18 @@ def serialize_item(x):
116123
x[idx] = serialize_item(it)
117124
if isinstance(x, dict) and not isinstance(x, _GUIDict):
118125
for k, v in x.items():
119-
x[k] = serialize_item(v)
126+
x[k] = serialize_item(v)
120127
return x
121128

122129
# handle Session and Message objects
123130
data = {k: serialize_item(v) for k, v in self.data.items()}
124131
ctxt = {k: serialize_item(v) for k, v in self.context.items()}
125-
return json.dumps({'type': self.msg_type,
126-
'data': data,
127-
'context': ctxt})
132+
133+
msg = json.dumps({'type': self.msg_type, 'data': data, 'context': ctxt})
134+
if self._secret_key:
135+
payload = encrypt_as_dict(msg)
136+
return json.dumps(payload)
137+
return msg
128138

129139
@staticmethod
130140
def deserialize(value):
@@ -143,6 +153,11 @@ def deserialize(value):
143153
value(str): This is the string received from the websocket
144154
"""
145155
obj = json.loads(value)
156+
if Message._secret_key:
157+
if 'ciphertext' in obj:
158+
obj = decrypt_from_dict(obj)
159+
elif not Message._allow_unencrypted:
160+
raise RuntimeError("got an unencrypted message, configured to refuse")
146161
return Message(obj.get('type') or '',
147162
obj.get('data') or {},
148163
obj.get('context') or {})
@@ -258,6 +273,23 @@ def utterance_remainder(self):
258273
return normalize(utt)
259274

260275

276+
def encrypt_as_dict(data, nonce=None):
277+
ciphertext, tag, nonce = encrypt(data, nonce=nonce)
278+
return {"ciphertext": hexlify(ciphertext).decode('utf-8'),
279+
"tag": hexlify(tag).decode('utf-8'),
280+
"nonce": hexlify(nonce).decode('utf-8')}
281+
282+
283+
def decrypt_from_dict(data):
284+
ciphertext = unhexlify(data["ciphertext"])
285+
if data.get("tag") is None: # web crypto
286+
ciphertext, tag = ciphertext[:-16], ciphertext[-16:]
287+
else:
288+
tag = unhexlify(data["tag"])
289+
nonce = unhexlify(data["nonce"])
290+
return decrypt(ciphertext, tag, nonce)
291+
292+
261293
def dig_for_message(max_records: int = 10) -> Optional[Message]:
262294
"""
263295
Dig Through the stack for message. Looks at the current stack
@@ -380,5 +412,6 @@ def extend(self, timeout):
380412
print(m2 == m1)
381413
print(isinstance(m1, _MycroftMessage))
382414
print(isinstance(m1, Message))
383-
print(isinstance(m2, _MycroftMessage)) # can't fix this one without the monkey patching, its defined in the class at mycroft_bus_client
415+
print(isinstance(m2,
416+
_MycroftMessage)) # can't fix this one without the monkey patching, its defined in the class at mycroft_bus_client
384417
print(isinstance(m2, Message))

0 commit comments

Comments
 (0)