OSDP.Net 5.2.1
A .NET library for implementing the Open Supervised Device Protocol (OSDP)
Loading...
Searching...
No Matches
OSDPPacketTextFormatter.cs
1using System;
2using System.Collections.Generic;
3using System.Text;
6
7namespace OSDP.Net.Tracing;
8
13{
16 public string FormatPacket(Packet packet, DateTime timestamp, TimeSpan? timeDelta = null)
17 {
18 if (packet == null)
19 throw new ArgumentNullException(nameof(packet));
20
21 var sb = new StringBuilder();
22
23 string deltaString = FormatTimeDelta(timeDelta);
24 string direction = packet.CommandType != null ? "ACU -> PD" : "PD -> ACU";
25 string type = (packet.CommandType?.GetDisplayName() ?? packet.ReplyType?.GetDisplayName()) ?? "Unknown";
26
27 sb.AppendLine($"{timestamp:yy-MM-dd HH:mm:ss.fff}{deltaString} {direction}: {type}");
28 sb.Append($" Address: {packet.Address} Sequence: {packet.Sequence}");
29 if (packet.IsSecureMessage)
30 {
31 sb.Append(packet.IsUsingDefaultKey ? " [Secure - Default Key]" : " [Secure]");
32 }
33 else
34 {
35 sb.Append(" [Clear Text]");
36 }
37 sb.AppendLine();
38
39 if (!packet.IsPayloadDecrypted)
40 {
41 sb.AppendLine(" *** Payload is encrypted - SCBK (Secure Channel Base Key) required to decrypt ***");
42 }
43 else
44 {
45 string secureChannelDetails = FormatSecureChannelHandshake(packet);
46 if (secureChannelDetails != null)
47 {
48 sb.AppendLine(secureChannelDetails);
49 }
50 else
51 {
52 var payloadData = packet.ParsePayloadData();
53 if (payloadData != null)
54 {
55 string payloadString = payloadData switch
56 {
57 byte[] data => $" {BitConverter.ToString(data)}",
58 _ => $" {payloadData}"
59 };
60 sb.AppendLine(payloadString);
61 }
62 }
63 }
64
65 sb.AppendLine();
66 return sb.ToString();
67 }
68
74 private static string FormatSecureChannelHandshake(Packet packet)
75 {
76 switch (packet.CommandType)
77 {
78 case CommandType.SessionChallenge:
79 return FormatSessionChallenge(packet);
80 case CommandType.ServerCryptogram:
81 return FormatServerCryptogram(packet);
82 }
83
84 switch (packet.ReplyType)
85 {
86 case ReplyType.CrypticData:
87 return FormatCrypticData(packet);
88 case ReplyType.InitialRMac:
89 return FormatInitialRMac(packet);
90 }
91
92 return null;
93 }
94
95 private static string FormatSessionChallenge(Packet packet)
96 {
97 var payload = packet.RawPayloadData;
98 var lines = new List<string>
99 {
100 " Secure Channel Handshake: osdp_CHLNG (Step 1 of 4 - ACU challenge)",
101 FormatSecurityBlockLine(packet, DescribeKeyType(packet))
102 };
103 lines.Add(payload.Length >= 8
104 ? $" RND.A (ACU random): {Hex(payload.Slice(0, 8))}"
105 : $" Payload: {Hex(payload)}");
106 return string.Join(Environment.NewLine, lines);
107 }
108
109 private static string FormatCrypticData(Packet packet)
110 {
111 var payload = packet.RawPayloadData;
112 var lines = new List<string>
113 {
114 " Secure Channel Handshake: osdp_CCRYPT (Step 2 of 4 - PD response)",
115 FormatSecurityBlockLine(packet, DescribeKeyType(packet))
116 };
117 if (payload.Length >= 32)
118 {
119 lines.Add($" cUID (PD unique ID): {Hex(payload.Slice(0, 8))}");
120 lines.Add($" RND.B (PD random): {Hex(payload.Slice(8, 8))}");
121 lines.Add($" Client cryptogram: {Hex(payload.Slice(16, 16))}");
122 }
123 else
124 {
125 lines.Add($" Payload: {Hex(payload)}");
126 }
127 return string.Join(Environment.NewLine, lines);
128 }
129
130 private static string FormatServerCryptogram(Packet packet)
131 {
132 return string.Join(Environment.NewLine,
133 " Secure Channel Handshake: osdp_SCRYPT (Step 3 of 4 - ACU server cryptogram)",
134 FormatSecurityBlockLine(packet, DescribeKeyType(packet)),
135 $" Server cryptogram: {Hex(packet.RawPayloadData)}");
136 }
137
138 private static string FormatInitialRMac(Packet packet)
139 {
140 return string.Join(Environment.NewLine,
141 " Secure Channel Handshake: osdp_RMAC_I (Step 4 of 4 - secure channel established)",
142 FormatSecurityBlockLine(packet, DescribeCryptogramAcceptance(packet)),
143 $" Initial R-MAC: {Hex(packet.RawPayloadData)}");
144 }
145
150 private static string FormatSecurityBlockLine(Packet packet, string interpretation)
151 {
152 string scsType = DescribeSecurityBlockType(packet.IncomingMessage.SecurityBlockType);
153 string data = FormatSecureBlockData(packet.IncomingMessage.SecureBlockData);
154 string suffix = string.IsNullOrEmpty(interpretation) ? string.Empty : $" ({interpretation})";
155 return $" Security Block: {scsType}, SEC_BLK_DATA: {data}{suffix}";
156 }
157
158 private static string DescribeSecurityBlockType(byte type)
159 {
160 return type switch
161 {
162 0x11 => "SCS_11",
163 0x12 => "SCS_12",
164 0x13 => "SCS_13",
165 0x14 => "SCS_14",
166 0x15 => "SCS_15",
167 0x16 => "SCS_16",
168 0x17 => "SCS_17",
169 0x18 => "SCS_18",
170 _ => $"0x{type:X2}"
171 };
172 }
173
178 private static string DescribeKeyType(Packet packet)
179 {
180 var secureBlockData = packet.IncomingMessage.SecureBlockData;
181 if (secureBlockData == null || secureBlockData.Length == 0)
182 {
183 return null;
184 }
185
186 return secureBlockData[0] == 0x00 ? "Default key (SCBK-D)" : "Configured key (SCBK)";
187 }
188
194 private static string DescribeCryptogramAcceptance(Packet packet)
195 {
196 var secureBlockData = packet.IncomingMessage.SecureBlockData;
197 if (secureBlockData == null || secureBlockData.Length == 0)
198 {
199 return null;
200 }
201
202 return secureBlockData[0] == 0x01
203 ? "Server cryptogram accepted"
204 : "Server cryptogram rejected";
205 }
206
207 private static string FormatSecureBlockData(byte[] data) =>
208 data == null || data.Length == 0 ? "(none)" : BitConverter.ToString(data);
209
210 private static string Hex(ReadOnlySpan<byte> data) => BitConverter.ToString(data.ToArray());
211
214 public string FormatError(byte[] rawData, DateTime timestamp, TimeSpan? timeDelta, string errorMessage)
215 {
216 if (rawData == null)
217 throw new ArgumentNullException(nameof(rawData));
218
219 var sb = new StringBuilder();
220 string deltaString = FormatTimeDelta(timeDelta);
221
222 sb.AppendLine($"{timestamp:yy-MM-dd HH:mm:ss.fff}{deltaString}");
223 sb.AppendLine($"*** Error parsing packet: {errorMessage} ***");
224 sb.AppendLine($" Raw data: {BitConverter.ToString(rawData)}");
225 sb.AppendLine();
226 return sb.ToString();
227 }
228
229 private static string FormatTimeDelta(TimeSpan? timeDelta)
230 {
231 return timeDelta.HasValue ? $" [ {timeDelta.Value:g} ]" : string.Empty;
232 }
233}
byte SecurityBlockType
Type of the security block, if there is one.
byte[] SecureBlockData
Raw security block bytes.
Information details of a message packet.
Definition Packet.cs:14
CommandType? CommandType
The type of command sent or null it is a reply.
Definition Packet.cs:62
bool IsPayloadDecrypted
Indicates whether the payload was successfully decrypted.
Definition Packet.cs:82
ReplyType? ReplyType
The type of reply sent or null it is a command.
Definition Packet.cs:67
bool IsSecureMessage
Indicates if the message was sent via an established secure channel.
Definition Packet.cs:87
ReadOnlySpan< byte > RawPayloadData
Raw bytes of the payload data.
Definition Packet.cs:215
object ParsePayloadData()
Parse the payload data into an object.
Definition Packet.cs:98
IncomingMessage IncomingMessage
The underlying incoming message.
Definition Packet.cs:47
bool IsUsingDefaultKey
Indicates if the secure channel is using the default key.
Definition Packet.cs:92
Default implementation of IPacketTextFormatter for formatting OSDP packets as human-readable text.
string FormatError(byte[] rawData, DateTime timestamp, TimeSpan? timeDelta, string errorMessage)
Formats a packet that failed to parse due to an error.Formatted text with error information and raw d...
string FormatPacket(Packet packet, DateTime timestamp, TimeSpan? timeDelta=null)
Formats a successfully parsed packet as text.Formatted text representation of the packet.
Interface for formatting OSDP packets as human-readable text.
ReplyType
Identifies the type of OSDP protocol reply (PD -> ACU) message.
Definition ReplyType.cs:7
CommandType
Identifies the type of OSDP protocol command (ACU -> PD) message.
Definition CommandType.cs:7