Skip to content

Commit

Permalink
Merge pull request #6 from Thomac02/master
Browse files Browse the repository at this point in the history
Merge fixes for secure channel
  • Loading branch information
ryanhz authored Aug 5, 2020
2 parents b965a7c + 474362b commit 613969b
Show file tree
Hide file tree
Showing 15 changed files with 89 additions and 9 deletions.
6 changes: 6 additions & 0 deletions osdp-diff/_bus_diff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
132,134c132
< if device.validate_secure_channel_establishment(reply):
< print("Secure session established.")
<
---
> device.validate_secure_channel_establishment(reply)
8 changes: 8 additions & 0 deletions osdp-diff/_command_diff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
41d40
< print("Building secure message...")
46,47c45,46
< additional_length = 4 + (2 if device.message_control.use_crc else 1)
< self.add_packet_length(command_buffer, additional_length)
---
> # additional_length = 4 + (device.message_control.use_crc ? 2 : 1)
> # self.add_packet_length(command_buffer, additional_length)
Empty file added osdp-diff/_connection_diff.txt
Empty file.
Empty file.
10 changes: 10 additions & 0 deletions osdp-diff/_device_diff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
33c33
<
---
>
38c38
< return ServerCryptogramCommand(self.address, self._secure_channel.server_cryptogram)
---
> return ServerCryptogramCommand(self.address, self._secure_channel.serverCryptogram)
59d58
< print("Cryptogram not accepted")
Empty file added osdp-diff/_message_diff.txt
Empty file.
8 changes: 8 additions & 0 deletions osdp-diff/_reply_diff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
111c111
< reply = UnknownReply(data, connection_id, issuing_command, device)
---
> reply = UnknownReply(data, connection_id, issuing_command, Device)
115d114
< print("Secure block data: ", self.secure_block_data[0])
159d157
< print("Extract reply data: ", self.extract_reply_data.hex())
8 changes: 8 additions & 0 deletions osdp-diff/_secure_channel_diff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
37c37
< if client_cryptogram != self.generate_key(self.server_random_number, client_random_number, self._enc):
---
> if client_cryptogram != self.generate_key(self.server_random_number, client_cryptogram, self._enc):
61c61
< self.server_random_number,
---
> self._server_random_number,
Empty file added osdp-diff/_types_diff.txt
Empty file.
4 changes: 3 additions & 1 deletion osdp/_bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ def process_reply(self, reply: Reply, device: Device):
if reply.type == ReplyType.CrypticData:
device.initialize_secure_channel(reply)
elif reply.type == ReplyType.InitialRMac:
device.validate_secure_channel_establishment(reply)
if device.validate_secure_channel_establishment(reply):
print("Secure session established.")


if self._on_reply_received is not None:
self._on_reply_received(reply)
Expand Down
33 changes: 31 additions & 2 deletions osdp/_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ def build_command(self, device) -> bytes:
command_buffer.append(self.command_code)

if device.is_security_established:
print("Building secure message...")
command_buffer.extend(self.encrypted_data(device))

# TODO: I don't think this needed
# include mac and crc/checksum in length before generating mac
# additional_length = 4 + (device.message_control.use_crc ? 2 : 1)
# self.add_packet_length(command_buffer, additional_length)
additional_length = 4 + (2 if device.message_control.use_crc else 1)
self.add_packet_length(command_buffer, additional_length)

command_buffer.extend(device.generate_mac(bytes(command_buffer), True)[0:4])
else:
Expand Down Expand Up @@ -365,3 +366,31 @@ def data(self) -> bytes:

def custom_command_update(self, command_buffer: bytearray):
pass

class KeySetCommand(Command):

def __init__(self, address: int, scbk: bytes):
self.address = address
self.scbk = scbk

@property
def command_code(self) -> int:
return 0x75

def security_control_block(self) -> bytes:
return bytes([0x02, 0x17])

def data(self) -> bytes:
return self.keyset_data()

def custom_command_update(self, command_buffer: bytearray):
pass

def keyset_data(self):
header = []
type = 0x01
len = 0x10
scbk = [0x41, 0x02, 0x31, 0x84, 0xF1, 0xA2, 0xDE, 0x7C, 0x32, 0x98, 0x01, 0xB8, 0x7B, 0x56, 0xB3, 0x60]
header.append(type)
header.append(len)
return bytes(header + scbk)
8 changes: 7 additions & 1 deletion osdp/_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from ._connection import OsdpConnection
from ._command import (
Command, IdReportCommand, DeviceCapabilitiesCommand, LocalStatusReportCommand, InputStatusReportCommand,
OutputStatusReportCommand, ReaderStatusReportCommand, OutputControlCommand, ReaderLedControlCommand
OutputStatusReportCommand, ReaderStatusReportCommand, OutputControlCommand, ReaderLedControlCommand,
KeySetCommand
)
from ._reply import Reply
from ._bus import Bus
Expand Down Expand Up @@ -63,6 +64,11 @@ def reader_led_control(self, connection_id: UUID, address: int, reader_led_contr
reply = self.send_command(connection_id, ReaderLedControlCommand(address, reader_led_controls))
return reply.type == ReplyType.Ack

def keyset(self, connection_id: UUID, address: int) -> bool:
reply = self.send_command(connection_id, KeySetCommand(address, bytes([])))
return reply.type == ReplyType.Ack


def is_online(self, connection_id: UUID, address: int) -> bool:
bus = self._buses.get(connection_id)
if bus is None:
Expand Down
5 changes: 3 additions & 2 deletions osdp/_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ def is_online(self) -> bool:
def get_next_command_data(self):
if self.message_control.sequence == 0:
return PollCommand(self.address)

if self._use_secure_channel and not self._secure_channel.is_initialized:
return SecurityInitializationRequestCommand(self.address, self._secure_channel.server_random_number)

if self._use_secure_channel and not self._secure_channel.is_established:
return ServerCryptogramCommand(self.address, self._secure_channel.serverCryptogram)
return ServerCryptogramCommand(self.address, self._secure_channel.server_cryptogram)

if self._commands.empty():
return PollCommand(self.address)
Expand All @@ -56,6 +56,7 @@ def initialize_secure_channel(self, reply):

def validate_secure_channel_establishment(self, reply) -> bool:
if not reply.secure_cryptogram_has_been_accepted():
print("Cryptogram not accepted")
return False

self._secure_channel.establish(reply.extract_reply_data)
Expand Down
4 changes: 3 additions & 1 deletion osdp/_reply.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,11 @@ def is_valid_reply(self) -> bool:

@staticmethod
def parse(data: bytes, connection_id: UUID, issuing_command: Command, device: Device):
reply = UnknownReply(data, connection_id, issuing_command, Device)
reply = UnknownReply(data, connection_id, issuing_command, device)
return reply

def secure_cryptogram_has_been_accepted(self) -> bool:
print("Secure block data: ", self.secure_block_data[0])
return self.secure_block_data[0] != 0

def match_issuing_command(self, command: Command) -> bool:
Expand Down Expand Up @@ -155,6 +156,7 @@ def __repr__(self):
return "Connection ID: {0} Address: {1} Type: {2}".format(self._connection_id, self.address, self.type)

def decrypt_data(self, device: Device) -> bytes:
print("Extract reply data: ", self.extract_reply_data.hex())
return device.decrypt_data(self.extract_reply_data)


Expand Down
4 changes: 2 additions & 2 deletions osdp/_secure_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def initialize(self, cuid: bytes, client_random_number: bytes, client_cryptogram
self.default_secure_channel_key
)

if client_cryptogram != self.generate_key(self.server_random_number, client_cryptogram, self._enc):
if client_cryptogram != self.generate_key(self.server_random_number, client_random_number, self._enc):
raise Exception("Invalid client cryptogram")

self._smac1 = self.generate_key(
Expand All @@ -58,7 +58,7 @@ def initialize(self, cuid: bytes, client_random_number: bytes, client_cryptogram
)
self.server_cryptogram = self.generate_key(
client_random_number,
self._server_random_number,
self.server_random_number,
self._enc
)
self.is_initialized = True
Expand Down

0 comments on commit 613969b

Please sign in to comment.