Skip to content
Snippets Groups Projects
Commit b7523f8f authored by jkerdreu's avatar jkerdreu
Browse files

Encoding & Decoding are Ok against my stack :)

git-svn-id: https://redmine.imt-atlantique.fr/svn/xaal/code/Python/branches/0.7@2275 b32b6428-25c9-4566-ad07-03861ab6144f
parent 13f6bbff
Branches
No related tags found
No related merge requests found
......@@ -18,7 +18,7 @@ DEF_LOG_LEVEL = 'DEBUG' # should be INFO|DEBUG|None
DEF_LOG_PATH = '/var/log/xaal' # where log are
# TBD : Move this stuff
STACK_VERSION = '0.5'
STACK_VERSION = 7
XAAL_BCAST_ADDR = '00000000-0000-0000-0000-000000000000'
......
......@@ -21,7 +21,7 @@
#
from .network import NetworkConnector
from .messages import MessageFactory
from .messages import MessageFactory,MessageType
from .exceptions import *
from . import config
from . import tools
......@@ -100,12 +100,12 @@ class Engine(object):
def send_request(self,dev,targets,action,body = None):
"""queue a new request"""
msg = self.msg_factory.build_msg(dev, targets, 'request', action, body)
msg = self.msg_factory.build_msg(dev, targets, MessageType.REQUEST, action, body)
self.queue_msg(msg)
def send_reply(self, dev, targets, action, body=None):
"""queue a new reply"""
msg = self.msg_factory.build_msg(dev, targets, 'reply', action, body)
msg = self.msg_factory.build_msg(dev, targets, MessageType.REPLY, action, body)
self.queue_msg(msg)
def send_error(self, dev, errcode, description=None):
......@@ -122,7 +122,7 @@ class Engine(object):
self.send_request(dev,targets,'getAttributes')
def send_notification(self,dev,action,body=None):
msg = self.msg_factory.build_msg(dev,[],"notify",action,body)
msg = self.msg_factory.build_msg(dev,[],MessageType.NOTIFY,action,body)
self.queue_msg(msg)
#####################################################
......@@ -138,7 +138,7 @@ class Engine(object):
"""Send a isAlive message, w/ devTypes filtering"""
body = {}
body['devTypes'] = devtypes
msg = self.msg_factory.build_msg(dev, [], "request", "isAlive", body)
msg = self.msg_factory.build_msg(dev, [], MessageType.REQUEST, "isAlive", body)
self.queue_msg(msg)
def process_alives(self):
......
......@@ -28,6 +28,7 @@ from . import config
from .exceptions import MessageError,MessageParserError
import ujson as json
import cbor
import datetime
import pysodium
import base64
......@@ -38,6 +39,8 @@ import codecs
import logging
logger = logging.getLogger(__name__)
from uuid import UUID
from enum import Enum
class MessageFactory(object):
"""Message Factory:
......@@ -55,59 +58,63 @@ class MessageFactory(object):
:return: return an xAAL msg ciphered and serialized in json
:rtype: json
"""
result = {}
result = []
# Format data msg to send
result['version'] = msg.version
result['targets'] = json.dumps(msg.targets)
result['timestamp'] = msg.timestamp
result.append(msg.version)
result.append(msg.timestamp[0])
result.append(msg.timestamp[1])
result.append(cbor.dumps(msg.targets))
# import pdb;pdb.set_trace()
# Format payload before ciphering
buf = []
buf.append(msg.source.bytes)
buf.append(msg.devtype)
buf.append(msg.msgtype.value)
buf.append(msg.action)
if msg.body:
buf = json.dumps({"header": msg.header, "body": msg.body})
else:
buf = json.dumps({"header": msg.header})
buf.append(msg.body)
payload = cbor.dumps(buf)
# Payload Ciphering: ciph
# Additionnal Data == json serialization of the targets array
payload = buf.encode('utf-8')
ad = json.dumps(msg.targets).encode('utf-8') # Additional Data
ad = result[3]
nonce = build_nonce(msg.timestamp)
ciph = pysodium.crypto_aead_chacha20poly1305_ietf_encrypt(payload, ad, nonce, self.cipher_key)
result.append(ciph)
# Add payload: base64 encoded of payload cipher
result['payload'] = base64.b64encode(ciph)
# Json serialization
message = json.dumps(result)
return codecs.encode(message)
logger.warn(result)
# CBOR serialization
pkt = cbor.dumps(result)
return pkt
def decode_msg(self, data):
"""Decode incoming Json data and De-Ciphering
:param data: data received from the multicast bus
:type data: json
:type data: cbor
:return: xAAL msg
:rtype: Message
"""
# Decode json incoming data
try:
data_rx = json.loads(data)
data_rx = cbor.loads(data)
except:
raise MessageParserError("Unable to parse JSON data")
raise MessageParserError("Unable to parse CBOR data")
#logger.warning(data_rx)
# Instanciate Message
msg = Message()
try:
msg.targets = json.loads(data_rx['targets'])
msg.version = data_rx['version']
msg.timestamp = data_rx['timestamp']
msg_time = data_rx['timestamp'][0]
except (KeyError, IndexError):
msg.version = data_rx[0]
msg_time = data_rx[1]
msg.targets = cbor.loads(data_rx[3])
msg.timestamp = [data_rx[1],data_rx[2]]
except KeyError:
raise MessageParserError("Bad Message, wrong fields")
# Replay attack, window fixed to CIPHER_WINDOW in seconds
now = build_timestamp()[0] # test done only on seconds ...
if msg_time < (now - config.cipher_window):
raise MessageParserError("Potential replay attack, message too old: %d sec" % round(now - msg_time) )
......@@ -115,46 +122,40 @@ class MessageFactory(object):
raise MessageParserError("Potential replay attack, message too young: %d sec" % round(now - msg_time))
# Payload De-Ciphering
ad = data_rx['targets'].encode("utf8") # Additional Data
nonce = build_nonce(data_rx['timestamp'])
# base64 decoding
if 'payload' in data_rx:
ciph = base64.b64decode(data_rx['payload'])
else:
try:
ciph = data_rx[4]
except IndexError:
raise MessageParserError("Bad Message, no payload found!")
# chacha20 deciphering
ad = data_rx[3]
nonce = build_nonce(msg.timestamp)
try:
pjson = pysodium.crypto_aead_chacha20poly1305_ietf_decrypt(ciph, ad, nonce, self.cipher_key)
clear = pysodium.crypto_aead_chacha20poly1305_ietf_decrypt(ciph, ad, nonce, self.cipher_key)
except :
raise MessageParserError("Unable to decrypt msg")
# Decode Json
# Decode payload
try:
payload = json.loads(pjson)
payload = cbor.loads(clear)
except:
raise MessageParserError("Unable to parse JSON data in payload after decrypt")
# Message unpacking header
if 'header' in payload:
head = {}
for (name, value) in payload["header"].items():
head[name] = value
msg.header = head
else:
raise MessageParserError("Bad Message, none header found in payload!")
raise MessageParserError("Unable to parse CBOR data in payload after decrypt")
try:
msg.source = UUID(bytes=payload[0])
msg.devtype = payload[1]
msg.msgtype = payload[2]
msg.action = payload[3]
except IndexError:
raise MessageParserError("Unable to parse payload headers")
if len(payload) == 5:
msg.body = payload[4]
# Message unpacking body
if 'body' in payload:
body = {}
for (name, value) in payload["body"].items():
body[name] = value
msg.body = body
""" TDB
# Sanity check incomming message
if not tools.is_valid_addr(msg.source):
raise MessageParserError("Wrong message source [%s]" % msg.source)
"""
return msg
#####################################################
......@@ -193,7 +194,7 @@ class MessageFactory(object):
"""
body = {}
body['timeout'] = timeout
message = self.build_msg(dev=dev,targets=[],msgtype="notify",action="alive",body=body)
message = self.build_msg(dev=dev,targets=[],msgtype=MessageType.NOTIFY,action="alive",body=body)
return message
def build_error_msg(self, dev, errcode, description=None):
......@@ -203,11 +204,17 @@ class MessageFactory(object):
body['code'] = errcode
if description:
body['description'] = description
message = self.build_msg(dev, [], "notify", "error", body)
message = self.build_msg(dev, [], MessageType.NOTIFY, "error", body)
return message
class MessageType(Enum):
NOTIFY = 0
REQUEST = 1
REPLY = 2
ERROR = 3
class Message(object):
""" Message object used for incomming & outgoint message """
......@@ -235,9 +242,11 @@ class Message(object):
if not isinstance(values, list):
raise MessageError("Expected a list for targetsList, got %s" % (type(values),))
for val in values:
if not tools.is_valid_addr(val):
raise MessageError("Bad target addr: %s" % val)
self.__targets = values
# TBD
#if not tools.is_valid_addr(val):
# raise MessageError("Bad target addr: %s" % val)
self.__targets.append(UUID(bytes=val))
#self.__targets = values
@property
def devtype(self):
......@@ -300,7 +309,7 @@ class Message(object):
if 'action' in self.header:
logger.debug("action: \t%s" % self.action)
if 'msgType' in self.header:
logger.debug("msgType: \t%s" % self.msgtype)
logger.debug("msgType: \t%s" % MessageType(self.msgtype))
if 'source' in self.header:
logger.debug("source: \t%s" % self.source)
logger.debug("version: \t%s" % self.version)
......@@ -320,44 +329,44 @@ class Message(object):
return " ".join(l)
def is_request(self):
if self.msgtype == 'request':
if self.msgtype == MessageType.REQUEST:
return True
return False
def is_reply(self):
if self.msgtype == 'reply':
if self.msgtype == MessageType.REPLY:
return True
return False
def is_notify(self):
if self.msgtype == 'notify':
if self.msgtype == MessageType.NOTIFY:
return True
return False
def is_alive(self):
if self.msgtype == 'notify' and self.action == 'alive':
if self.msgtype == MessageType.NOTIFY and self.action == 'alive':
return True
return False
def is_request_isalive(self,devtype="any.any"):
if self.msgtype == 'request' and self.action == 'isAlive':
if self.is_request() and self.action == 'isAlive':
target = self.body.get('devTypes',None)
if target == devtype:
return True
return False
def is_attributes_change(self):
if self.msgtype == 'notify' and self.action == 'attributesChange':
if self.is_notify() and self.action == 'attributesChange':
return True
return False
def is_get_attribute_reply(self):
if self.msgtype == 'reply' and self.action == 'getAttributes':
if self.is_reply() and self.action == 'getAttributes':
return True
return False
def is_get_description_reply(self):
if self.msgtype == 'reply' and self.action == 'getDescription':
if self.is_reply() and self.action == 'getDescription':
return True
return False
......
......@@ -65,20 +65,23 @@ def new_cfg(app_name):
return cfg
def get_random_uuid():
return str(uuid.uuid1())
return uuid.uuid1()
def is_valid_addr(val):
if val == None:
return False
if re.match(XAAL_ADDR_PATTERN,val):
# TBD
#if re.match(XAAL_ADDR_PATTERN,val):
# return True
#return False
return True
return False
def is_valid_devtype(val):
if re.match(XAAL_DEVTYPE_PATTERN,val):
return True
return False
def pass2key(passphrase):
"""Generate key from passphrase using libsodium
crypto_pwhash_scryptsalsa208sha256 func
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment