README.md
Rendering markdown...
"""Protocol of communication.
This module contain classes for generating and encrypting messages.
"""
import binascii
import hmac
import math
from hashlib import pbkdf2_hmac, sha256
from Crypto.Cipher import AES
class Protocol:
"""Protocol encoding/decoding for Norton Core WiFi router."""
@staticmethod
def encode_set_setting(setting_type, value, serial_number, init_vec):
"""Construct a message to set a setting.
:param setting_type: Setting type, see paper for details.
:param value: Value of the setting.
:param serial_number: Device serial number, uses as key for encryption.
:param init_vec: IV for encryption.
:return: Constructed message.
"""
enc = Encryption(serial_number, init_vec)
data_set = DataSet(setting_type, value, enc)
return data_set.get_whole_request()
@staticmethod
def encode_request_unlock(serial_number, init_vec):
"""Construct a message to unlock Norton Core Secure Router BLE channel. Format of message:
[type_of_message, length_of_data, nonce] ([0x06, 0x10, nonce]).
:param serial_number: Device serial number, uses as key for encryption.
:param init_vec: IV for encryption.
:return: Constructed message.
"""
enc = Encryption(serial_number, init_vec)
msg = bytearray([0x06, 0x10]) + enc.get_nonce()
return msg
@staticmethod
def encode_request_iv(protocol_version):
"""Construct a message to get IV for encryption.
:param protocol_version: Protocol version of BLE communication.
:return: Constructed message.
"""
protocol_version = float(protocol_version)
minor_ver, major_ver = math.modf(protocol_version)
return bytearray([0x00, int(major_ver), int(minor_ver * 10)])
@staticmethod
def decode_ack(buffer):
"""Decode a Ack response.
:param buffer: Message (Ack response) for decoding.
:return: Length of sent data.
"""
return buffer[2]
@staticmethod
def decode_iv(buffer):
"""Decode a message with IV.
:param buffer: Message for decoding.
:return: Initialization vector for encryption.
"""
return buffer[2:18]
class DataSet:
"""Class for generating encrypted message for set a setting: username, password, IP, DNS and
etc.
"""
_type_request = 0x01
def __init__(self, setting_type, value, encryptor):
"""
:param setting_type: Type of setting.
:param value: Value of setting.
:param encryptor: Encryption object for encrypting message.
"""
value = bytes(value, 'utf-8')
self._setting_type = setting_type
self._data = value
self._data += sha256(value).digest()[:4]
self._data += self._get_padding(self._data)
self._data = encryptor.encrypt(self._data)
self._total_len = len(self._data)
self._sent_len = 0
def get_chunk_request(self):
"""Get part of request. Uses for testing.
:return: Chunk of request.
"""
if self._sent_len == self._total_len:
return None
packet_len = min(self._total_len - self._sent_len, 13)
packet = bytearray(2 + 3)
packet[0] = self._type_request
packet[1] = packet_len + 3 | 0x80
packet[2] = packet_len + self._sent_len
packet[3] = self._total_len
packet[4] = self._setting_type
msg = self._data[self._sent_len:self._sent_len + packet_len]
packet += msg
self._sent_len += packet_len
return packet
def get_whole_request(self):
"""Get whole request without splitting.
:return: Whole request.
"""
request = bytearray()
chunk_request = self.get_chunk_request()
while chunk_request:
request += chunk_request
chunk_request = self.get_chunk_request()
return request
@staticmethod
def _get_padding(data):
length = 16 - (len(data) % 16)
return bytes([length]) * length
class Encryption:
"""Class uses for getting nonce, keys and encrypt messages."""
def __init__(self, serial_number, iv):
"""
:param serial_number: Device serial number, uses as key.
:param iv: Initialization vector.
"""
self._pwd = bytes(serial_number, 'utf-8')
self._iv = iv
self._hmac = hmac.new(self._iv, self._pwd, sha256).digest()[-16:]
self._dk = pbkdf2_hmac('sha256', self._pwd, self._iv, 1000, 16)
def __str__(self) -> str:
return "Enc{{\npwd: {}\
\niv: {}\
\nhmac: {}\
\ndk: {}\
\n}}".format(self._pwd,
binascii.hexlify(self._iv),
binascii.hexlify(self._hmac),
binascii.hexlify(self._dk))
def get_nonce(self):
"""Get nonce (HMAC) for unlocking Norton Core Router.
:return: Generated HMAC.
"""
return self._hmac
def encrypt(self, data):
"""Encrypt data with AES, key - device serial number.
:param data: Plain text data.
:return: Encrypted data.
"""
aes_enc = AES.new(self._dk, AES.MODE_CBC, bytes(self._iv))
return aes_enc.encrypt(data)