OSDP.Net 5.2.1
A .NET library for implementing the Open Supervised Device Protocol (OSDP)
Loading...
Searching...
No Matches
MessageSecureChannel.cs
1using System;
2using System.Linq;
3using Microsoft.Extensions.Logging;
4
6
20{
31 protected MessageSecureChannel(SecurityContext context = null, ILoggerFactory loggerFactory = null)
32 {
33 Context = context ?? new();
34 Logger = loggerFactory?.CreateLogger(GetType());
35 }
36
40 protected ILogger Logger { get; }
41
45 protected SecurityContext Context { get; }
46
48 public bool IsSecurityEstablished => Context.IsSecurityEstablished;
49
51 public bool IsInitialized => Context.IsInitialized;
52
54 public byte[] ServerRandomNumber => Context.ServerRandomNumber;
55
57 public byte[] ServerCryptogram => Context.ServerCryptogram;
58
60 public bool IsUsingDefaultKey => Context.IsUsingDefaultKey;
61
63 public abstract byte[] DecodePayload(byte[] payload);
64
66 public abstract void EncodePayload(byte[] payload, Span<byte> destination);
67
69 public abstract ReadOnlySpan<byte> GenerateMac(ReadOnlySpan<byte> message, bool isIncoming);
70
72 public void InitializeACU(byte[] clientRandomNumber, byte[] clientCryptogram)
73 {
74 Context.InitializeACU(clientRandomNumber, clientCryptogram);
75 }
76
78 public void ResetSecureChannelSession() => Context.Reset();
79
81 public void Establish(byte[] rmac) => Context.Establish(rmac);
82
89 protected byte[] GenerateCommandMac(ReadOnlySpan<byte> message) => Context.CMac = GenerateMac(message, Context.RMac);
90
97 protected byte[] GenerateReplyMac(ReadOnlySpan<byte> message) => Context.RMac = GenerateMac(message, Context.CMac);
98
99 private byte[] GenerateMac(ReadOnlySpan<byte> message, byte[] iv)
100 {
102 {
103 throw new SecureChannelRequired();
104 }
105
106 using var crypto = Context.CreateCypher( false, Context.SMac1);
107 crypto.IV = iv;
108
109 var cursor = message;
110 while (cursor.Length > 0)
111 {
112 byte[] block;
113
114 if (cursor.Length < 16)
115 {
116 block = new byte[16];
117 cursor.CopyTo(block);
118 block[cursor.Length] = Message.FirstPaddingByte;
119 cursor = cursor.Slice(cursor.Length);
120 crypto.Key = Context.SMac2;
121 }
122 else
123 {
124 block = cursor.Slice(0, 16).ToArray();
125 cursor = cursor.Slice(16);
126 if (cursor.Length == 0) crypto.Key = Context.SMac2;
127 }
128
129 using var encryptor = crypto.CreateEncryptor();
130 crypto.IV = encryptor.TransformFinalBlock(block, 0, block.Length);
131 }
132
133 return crypto.IV;
134 }
135
142 protected byte[] DecodePayload(byte[] payload, byte[] iv)
143 {
145 {
146 throw new SecureChannelRequired();
147 }
148
149 if (payload.Length == 0)
150 {
151 return Array.Empty<byte>();
152 }
153 if (payload.Length % 16 != 0)
154 {
155 throw new Exception($"Unexpected payload length: {payload.Length}");
156 }
157
158 using var crypto = Context.CreateCypher( false, Context.Enc);
159 crypto.IV = iv.Select(b => (byte)~b).ToArray();
160
161 using var encryptor = crypto.CreateDecryptor();
162 return encryptor.TransformFinalBlock(payload, 0, payload.Length);
163 }
164
172 protected void EncodePayload(byte[] payload, byte[] iv, Span<byte> destination)
173 {
175 {
176 throw new SecureChannelRequired();
177 }
178
179 if (payload.Length > 0)
180 {
181 if (payload.Length % 16 != 0)
182 {
183 throw new Exception($"Unexpected payload length: {payload.Length}");
184 }
185
186 using var crypto = Context.CreateCypher( false, Context.Enc);
187 crypto.IV = iv.Select(b => (byte)~b).ToArray();
188
189 using var encryptor = crypto.CreateEncryptor();
190 encryptor.TransformFinalBlock(payload, 0, payload.Length).CopyTo(destination);
191 }
192 }
193
195 public ReadOnlySpan<byte> PadTheData(ReadOnlySpan<byte> data)
196 {
197 const byte cryptoLength = 16;
198 const byte paddingStart = 0x80;
199
200 int dataLength = data.Length + 1;
201 int paddingLength = dataLength + (cryptoLength - dataLength % cryptoLength) % cryptoLength;
202
203 Span<byte> buffer = stackalloc byte[paddingLength];
204 buffer.Clear();
205
206 var cursor = buffer.Slice(0);
207
208 data.CopyTo(cursor);
209 cursor = cursor.Slice(data.Length);
210
211 cursor[0] = paddingStart;
212
213 return buffer.ToArray();
214 }
215}
A base message class for sending or replying.
Definition Message.cs:12
const byte FirstPaddingByte
While padding bytes are generally 0x00, the first byte of padding is not.
Definition Message.cs:39
byte[] DecodePayload(byte[] payload, byte[] iv)
Decodes the payload.
byte[] GenerateCommandMac(ReadOnlySpan< byte > message)
Generates a MAC for a command message.
bool IsSecurityEstablished
Indicates whether or not a secure channel has been established on the current message channel.
byte[] GenerateReplyMac(ReadOnlySpan< byte > message)
Generates a MAC for a reply message.
void Establish(byte[] rmac)
Establishes the secure channel with the provided RMAC.
void ResetSecureChannelSession()
Resets the secure channel session by creating a new random number.
bool IsUsingDefaultKey
Gets a value indicating whether the secure channel is using the default key.
byte[] ServerRandomNumber
Represents the server random number used in the secure channel context.
void EncodePayload(byte[] payload, Span< byte > destination)
Encrypts the payload using the secure channel context.This function can only be called once IsSecurit...
ReadOnlySpan< byte > PadTheData(ReadOnlySpan< byte > data)
Pads the given data with padding bytes to make it a multiple of a specified length....
void EncodePayload(byte[] payload, byte[] iv, Span< byte > destination)
Encodes the payload.
ReadOnlySpan< byte > GenerateMac(ReadOnlySpan< byte > message, bool isIncoming)
Generates a new Message Authentication Code.Note this command IS NOT idempotent as every time it is c...
MessageSecureChannel(SecurityContext context=null, ILoggerFactory loggerFactory=null)
Initializes a new instance of MessageSecureChannel class.
byte[] ServerCryptogram
Represents the secure channel cryptogram used by the server.This property is used to retrieve the ser...
bool IsInitialized
Gets a value indicating whether the secure channel is initialized.true if the secure channel is initi...
SecurityContext Context
Security state used by the channel.
byte[] DecodePayload(byte[] payload)
Decodes the payload using the secure channel context.This function can only be called once IsSecurity...
void InitializeACU(byte[] clientRandomNumber, byte[] clientCryptogram)
Initializes the Access Control Unit (ACU) in the secure channel context.
Security context used within MessageSecureChannel.
Represents the secure channel context within which a message is packed/unpacked.