25
25
import re
26
26
from copy import deepcopy
27
27
from typing import Optional
28
- from ovos_utils . log import LOG
28
+ from binascii import hexlify , unhexlify
29
29
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
30
33
31
34
try :
32
35
from lingua_franca .parse import normalize
35
38
def normalize (text , * args , ** kwargs ):
36
39
return text
37
40
38
-
39
41
try :
40
42
from mycroft_bus_client .message import Message as _MsgBase , \
41
43
CollectionMessage as _CollectionMsgBase
@@ -49,6 +51,7 @@ def normalize(text, *args, **kwargs):
49
51
class _MsgBase :
50
52
pass
51
53
54
+
52
55
class _CollectionMsgBase (_MsgBase ):
53
56
pass
54
57
@@ -77,6 +80,10 @@ class Message(_MsgBase, metaclass=_MessageMeta):
77
80
context: info about the message not part of data such as source,
78
81
destination or domain.
79
82
"""
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 )
80
87
81
88
def __init__ (self , msg_type , data = None , context = None ):
82
89
"""Used to construct a message object
@@ -116,15 +123,18 @@ def serialize_item(x):
116
123
x [idx ] = serialize_item (it )
117
124
if isinstance (x , dict ) and not isinstance (x , _GUIDict ):
118
125
for k , v in x .items ():
119
- x [k ] = serialize_item (v )
126
+ x [k ] = serialize_item (v )
120
127
return x
121
128
122
129
# handle Session and Message objects
123
130
data = {k : serialize_item (v ) for k , v in self .data .items ()}
124
131
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
128
138
129
139
@staticmethod
130
140
def deserialize (value ):
@@ -143,6 +153,11 @@ def deserialize(value):
143
153
value(str): This is the string received from the websocket
144
154
"""
145
155
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" )
146
161
return Message (obj .get ('type' ) or '' ,
147
162
obj .get ('data' ) or {},
148
163
obj .get ('context' ) or {})
@@ -258,6 +273,23 @@ def utterance_remainder(self):
258
273
return normalize (utt )
259
274
260
275
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
+
261
293
def dig_for_message (max_records : int = 10 ) -> Optional [Message ]:
262
294
"""
263
295
Dig Through the stack for message. Looks at the current stack
@@ -380,5 +412,6 @@ def extend(self, timeout):
380
412
print (m2 == m1 )
381
413
print (isinstance (m1 , _MycroftMessage ))
382
414
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
384
417
print (isinstance (m2 , Message ))
0 commit comments