OSDP.Net 5.2.1
A .NET library for implementing the Open Supervised Device Protocol (OSDP)
Loading...
Searching...
No Matches
IncomingMessage.cs
1
2using System;
3using System.Collections.Generic;
4using System.Linq;
6
7namespace OSDP.Net.Messages
8{
14 public class IncomingMessage : Message
15 {
16 private const ushort MessageHeaderSize = 6;
17 private readonly byte[] _originalMessage;
18
24 public IncomingMessage(ReadOnlySpan<byte> data, IMessageSecureChannel channel)
25 {
27
28 // TODO: way too much copying in this code, simplify it.
29 _originalMessage = data.ToArray();
30
31 Address = (byte)(data[1] & AddressMask);
32 MessageType = data[1] < 0x80 ? MessageType.Command : MessageType.Reply;
33 Sequence = (byte)(data[4] & 0x03);
34 IsUsingCrc = Convert.ToBoolean(data[4] & 0x04);
35 ushort replyMessageFooterSize = (ushort)(IsUsingCrc ? 2 : 1);
36 bool isSecureControlBlockPresent = Convert.ToBoolean(data[4] & 0x08);
37 byte secureBlockSize = (byte)(isSecureControlBlockPresent ? data[5] : 0);
38 SecurityBlockType = (byte)(isSecureControlBlockPresent ? data[6] : 0);
39 int messageLength = data.Length - (IsUsingCrc ? 6 : 5);
40 if (isSecureControlBlockPresent)
41 {
42 SecureBlockData = data.Slice(MessageHeaderSize + 1, secureBlockSize - 2).ToArray();
43 Mac = data.Slice(messageLength, MacSize).ToArray();
44 }
45
46 Type = data[MsgTypeIndex + secureBlockSize];
47
48 Payload = data.Slice(MessageHeaderSize + secureBlockSize, data.Length -
49 MessageHeaderSize - secureBlockSize - replyMessageFooterSize -
50 (IsSecureMessage ? MacSize : 0)).ToArray();
51 if (Payload.Length > 0 && HasSecureData)
52 {
53 if (channel.IsSecurityEstablished)
54 {
55 var paddedPayload = channel.DecodePayload(Payload);
56 var lastByteIdx = Payload.Length;
57 while (lastByteIdx > 0 && paddedPayload[--lastByteIdx] != FirstPaddingByte)
58 {
59 }
60
61 // lastByteIdx now indexes the 0x80 pad marker. Index 0 is valid: it means the
62 // command carried an empty payload (e.g. an encrypted osdp_LSTAT), so the whole
63 // decrypted block is just the padding marker. Only a block with no 0x80 at all
64 // is genuinely malformed.
65 if (paddedPayload[lastByteIdx] != FirstPaddingByte)
66 throw new Exception("The encrypted payload is missing a padding byte");
67
68 Payload = paddedPayload.AsSpan().Slice(0, lastByteIdx).ToArray();
69 }
70 else
71 {
72 IsPayloadDecrypted = false;
73 }
74 }
75
77 ? CalculateCrc(data.Slice(0, data.Length - 2)) ==
78 ConvertBytesToUnsignedShort(data.Slice(data.Length - 2, 2))
79 : CalculateChecksum(data.Slice(0, data.Length - 1).ToArray()) == data[data.Length - 1];
80
82 {
83 if (channel.IsSecurityEstablished)
84 {
85 var mac = channel.GenerateMac(data.Slice(0, messageLength).ToArray(), true);
86 IsValidMac = !IsSecureMessage || mac.Slice(0, MacSize).SequenceEqual(Mac?.ToArray());
87 }
88 else
89 {
90 IsValidMac = false;
91 }
92 }
93 else
94 {
95 IsValidMac = true;
96 }
97 }
98
102 public bool IsUsingCrc { get; }
103
107 public byte Type { get; }
108
112 public byte Sequence { get; }
113
114 internal Control ControlBlock => new(Sequence, IsUsingCrc, IsSecureMessage);
115
119 public bool IsSecureMessage => SecureSessionMessages.Contains(SecurityBlockType) && IsDataSecure;
120
125 public bool IsValidMac { get; }
126
135 public bool IsUsingDefaultKey { get; internal set; }
136
146 public bool HasSecureData =>
147 SecurityBlockType == (byte)SecureChannel.SecurityBlockType.CommandMessageWithDataSecurity ||
148 SecurityBlockType == (byte)SecureChannel.SecurityBlockType.ReplyMessageWithDataSecurity;
149
158 public bool IsPayloadDecrypted { get; private set; } = true;
159
163 public byte[] Payload { get; }
164
169 public ReadOnlySpan<byte> OriginalMessageData => _originalMessage;
170
174 public byte SecurityBlockType { get; }
175
179 public byte[] SecureBlockData { get; }
180
182 protected override ReadOnlySpan<byte> Data() => Payload.ToArray();
183
184 private bool IsDataSecure => Payload == null || Payload.Length == 0 ||
185 SecurityBlockType == (byte)SecureChannel.SecurityBlockType.ReplyMessageWithDataSecurity ||
186 SecurityBlockType == (byte)SecureChannel.SecurityBlockType.CommandMessageWithDataSecurity;
187
188 private IEnumerable<byte> Mac { get; }
189
194 public bool IsDataCorrect { get; }
195
196 private static IEnumerable<byte> SecureSessionMessages => new[]
197 {
198 (byte)SecureChannel.SecurityBlockType.CommandMessageWithNoDataSecurity,
199 (byte)SecureChannel.SecurityBlockType.ReplyMessageWithNoDataSecurity,
200 (byte)SecureChannel.SecurityBlockType.CommandMessageWithDataSecurity,
201 (byte)SecureChannel.SecurityBlockType.ReplyMessageWithDataSecurity,
202 };
203
213 }
214}
override ReadOnlySpan< byte > Data()
bool IsValidMac
Indicates if the message has a valid MAC signature which was validated via local message channel cont...
bool HasSecureData
Determines whether the incoming message has secure data.
bool IsPayloadDecrypted
Indicates whether the payload was successfully decrypted.
byte Type
Command/reply code of the message.
byte Sequence
Message sequence number.
bool SecureCryptogramHasBeenAccepted()
Checks whether the secure cryptogram has been accepted.
IncomingMessage(ReadOnlySpan< byte > data, IMessageSecureChannel channel)
Creates a new instance of IncomingMessage class.
byte SecurityBlockType
Type of the security block, if there is one.
ReadOnlySpan< byte > OriginalMessageData
Original byte data which includes the header, security control block, payload, MAC and CRC/Checksum s...
bool IsDataCorrect
Gets a value indicating whether the data in the incoming message is correct.
byte[] SecureBlockData
Raw security block bytes.
bool IsUsingCrc
If true, the message has a CRC suffix; otherwise it has a checksum.
bool IsSecureMessage
Indicates if the message was sent via an established secure channel.
bool IsUsingDefaultKey
Gets whether the incoming message is using the default key.
byte[] Payload
Returns the raw message payload.
A base message class for sending or replying.
Definition Message.cs:12
const ushort MacSize
Size in bytes of MAC signature in message suffix when the secure channel has been established.
Definition Message.cs:32
const ushort MsgTypeIndex
Index of command/reply code within the message header.
Definition Message.cs:26
const byte FirstPaddingByte
While padding bytes are generally 0x00, the first byte of padding is not.
Definition Message.cs:39
MessageType MessageType
Type of message.
Definition Message.cs:73
byte Address
Address assigned to the device.
Definition Message.cs:68
static ushort CalculateCrc(ReadOnlySpan< byte > packet)
Calculates the CRC from the packet data.
Definition Message.cs:111
static byte CalculateChecksum(byte[] packet)
Calculates the checksum from the packet data.
Definition Message.cs:127
const byte AddressMask
OSDP message address mask.
Definition Message.cs:16
Represents the secure channel context within which a message is packed/unpacked.
bool IsUsingDefaultKey
Gets a value indicating whether the secure channel is using the default key.
ReadOnlySpan< byte > GenerateMac(ReadOnlySpan< byte > message, bool isIncoming)
Generates a new Message Authentication Code.
byte[] DecodePayload(byte[] payload)
Decodes the payload using the secure channel context.
bool IsSecurityEstablished
Indicates whether or not a secure channel has been established on the current message channel.
@ Command
Command message.
Definition MessageType.cs:9