diff --git a/SharpSnmpLib/Header.cs b/SharpSnmpLib/Header.cs
index 92563519..c3a9c66b 100644
--- a/SharpSnmpLib/Header.cs
+++ b/SharpSnmpLib/Header.cs
@@ -76,14 +76,27 @@ public Header(ISnmpData data)
/// The message id.
/// Size of the max message.
/// The security level.
+ /// The security model.
/// If you want an empty header, please use .
- public Header(Integer32? messageId, Integer32 maxMessageSize, Levels securityLevel)
+ public Header(Integer32? messageId, Integer32 maxMessageSize, Levels securityLevel, Integer32 securityModel)
{
- _messageId = messageId;
+ _messageId = messageId;
_maxSize = maxMessageSize ?? throw new ArgumentNullException(nameof(maxMessageSize));
SecurityLevel = securityLevel;
_flags = new OctetString(SecurityLevel);
- _securityModel = DefaultSecurityModel;
+ _securityModel = securityModel;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message id.
+ /// Size of the max message.
+ /// The security level.
+ /// If you want an empty header, please use .
+ public Header(Integer32? messageId, Integer32 maxMessageSize, Levels securityLevel)
+ : this(messageId, maxMessageSize, securityLevel, DefaultSecurityModel)
+ {
}
///
@@ -102,6 +115,15 @@ public Header(Integer32? messageId, Integer32 maxMessageSize, Levels securityLev
/// The message ID.
public int MessageId => _messageId == null ? throw new InvalidOperationException() : _messageId.ToInt32();
+ ///
+ /// Gets the security model.
+ ///
+ /// The security model.
+ public SecurityModel SecurityModel
+ {
+ get { return (SecurityModel)_securityModel.ToInt32(); }
+ }
+
#region ISegment Members
///
diff --git a/SharpSnmpLib/Messaging/Discoverer.cs b/SharpSnmpLib/Messaging/Discoverer.cs
index 263685e8..68a2b1a8 100644
--- a/SharpSnmpLib/Messaging/Discoverer.cs
+++ b/SharpSnmpLib/Messaging/Discoverer.cs
@@ -317,7 +317,7 @@ public async Task DiscoverAsync(VersionCode version, IPEndPoint broadcastAddress
using var udp = new Socket(addressFamily, SocketType.Dgram, ProtocolType.Udp);
udp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
var buffer = new ArraySegment(bytes);
- await udp.SendToAsync(buffer, SocketFlags.None, broadcastAddress);
+ await udp.SendToAsync(buffer, SocketFlags.None, broadcastAddress).ConfigureAwait(false);
var activeBefore = Interlocked.CompareExchange(ref _active, Active, Inactive);
if (activeBefore == Active)
@@ -341,7 +341,8 @@ public async Task DiscoverAsync(VersionCode version, IPEndPoint broadcastAddress
#else
await Task.WhenAny(
ReceiveAsync(udp),
- Task.Delay(interval));
+ Task.Delay(interval))
+ .ConfigureAwait(false);
#endif
Interlocked.CompareExchange(ref _active, Inactive, Active);
try
@@ -370,7 +371,7 @@ private async Task ReceiveAsync(Socket socket)
EndPoint remote = new IPEndPoint(IPAddress.Any, 0);
var buffer = new byte[_bufferSize];
- var result = await socket.ReceiveMessageFromAsync(new ArraySegment(buffer), SocketFlags.None, remote);
+ var result = await socket.ReceiveMessageFromAsync(new ArraySegment(buffer), SocketFlags.None, remote).ConfigureAwait(false);
await Task.Factory.StartNew(() => HandleMessage(buffer, result.ReceivedBytes, (IPEndPoint) result.RemoteEndPoint))
.ConfigureAwait(false);
}
diff --git a/SharpSnmpLib/Messaging/GetBulkRequestMessage.cs b/SharpSnmpLib/Messaging/GetBulkRequestMessage.cs
index e5a92981..1cb0d63f 100644
--- a/SharpSnmpLib/Messaging/GetBulkRequestMessage.cs
+++ b/SharpSnmpLib/Messaging/GetBulkRequestMessage.cs
@@ -107,6 +107,69 @@ public GetBulkRequestMessage(VersionCode version, int messageId, int requestId,
{
}
+ ///
+ /// Creates a with a specific .
+ ///
+ /// The version.
+ /// The message id.
+ /// The request id.
+ /// Context name.
+ /// The non repeaters.
+ /// The max repetitions.
+ /// The variables.
+ /// The privacy provider.
+ /// Size of the max message.
+ public GetBulkRequestMessage(VersionCode version, int messageId, int requestId, OctetString contextName, int nonRepeaters, int maxRepetitions, IList variables, IPrivacyProvider privacy, int maxMessageSize)
+ {
+ if (variables == null)
+ {
+ throw new ArgumentNullException(nameof(variables));
+ }
+
+ if (contextName == null)
+ {
+ throw new ArgumentNullException(nameof(contextName));
+ }
+
+ if (version != VersionCode.V3)
+ {
+ throw new ArgumentException("Only v3 is supported.", nameof(version));
+ }
+
+ if (privacy == null)
+ {
+ throw new ArgumentNullException(nameof(privacy));
+ }
+
+ if (nonRepeaters > variables.Count)
+ {
+ throw new ArgumentException("nonRepeaters should not be greater than variable count.", nameof(nonRepeaters));
+ }
+
+ if (maxRepetitions < 1)
+ {
+ throw new ArgumentException("maxRepetitions should be greater than 0.", nameof(maxRepetitions));
+ }
+
+ Version = version;
+ Privacy = privacy;
+
+ // TODO: define more constants.
+ Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable, new Integer32((int)SecurityModel.Tsm));
+ Parameters = SecurityParameters.Empty;
+
+ var pdu = new GetBulkRequestPdu(
+ requestId,
+ nonRepeaters,
+ maxRepetitions,
+ variables);
+ var contextEngineId = OctetString.Empty;
+ Scope = new Scope(contextEngineId, contextName, pdu);
+
+ Privacy.ComputeHash(Version, Header, Parameters, Scope);
+ _bytes = this.PackMessage(null).ToBytes();
+ }
+
///
/// Creates a with a specific .
///
@@ -121,7 +184,8 @@ public GetBulkRequestMessage(VersionCode version, int messageId, int requestId,
/// The privacy provider.
/// Size of the max message.
/// The report.
- public GetBulkRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, int nonRepeaters, int maxRepetitions, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report)
+ /// The security model.
+ public GetBulkRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, int nonRepeaters, int maxRepetitions, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report, SecurityModel securityModel)
{
if (variables == null)
{
@@ -160,16 +224,25 @@ public GetBulkRequestMessage(VersionCode version, int messageId, int requestId,
Version = version;
Privacy = privacy ?? throw new ArgumentNullException(nameof(privacy));
- Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable);
+ Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable, new Integer32((int)securityModel));
var parameters = report.Parameters;
var authenticationProvider = Privacy.AuthenticationProvider;
- Parameters = new SecurityParameters(
- parameters.EngineId,
- parameters.EngineBoots,
- parameters.EngineTime,
- userName,
- authenticationProvider.CleanDigest,
- Privacy.Salt);
+
+ if (securityModel == SecurityModel.Tsm)
+ {
+ Parameters = SecurityParameters.Empty;
+ }
+ else
+ {
+ Parameters = new SecurityParameters(
+ parameters.EngineId,
+ parameters.EngineBoots,
+ parameters.EngineTime,
+ userName,
+ authenticationProvider.CleanDigest,
+ Privacy.Salt);
+ }
+
var pdu = new GetBulkRequestPdu(
requestId,
nonRepeaters,
@@ -188,6 +261,24 @@ public GetBulkRequestMessage(VersionCode version, int messageId, int requestId,
_bytes = this.PackMessage(null).ToBytes();
}
+ ///
+ /// Creates a with a specific .
+ ///
+ /// The version.
+ /// The message id.
+ /// The request id.
+ /// Name of the user.
+ /// Context name.
+ /// The non repeaters.
+ /// The max repetitions.
+ /// The variables.
+ /// The privacy provider.
+ /// Size of the max message.
+ /// The report.
+ public GetBulkRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, int nonRepeaters, int maxRepetitions, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report)
+ : this(version, messageId, requestId, userName, contextName, nonRepeaters, maxRepetitions, variables, privacy, maxMessageSize, report, SecurityModel.Usm)
+ {
+ }
///
/// Creates a with a specific .
diff --git a/SharpSnmpLib/Messaging/GetNextRequestMessage.cs b/SharpSnmpLib/Messaging/GetNextRequestMessage.cs
index cc8e2123..9d1ca3be 100644
--- a/SharpSnmpLib/Messaging/GetNextRequestMessage.cs
+++ b/SharpSnmpLib/Messaging/GetNextRequestMessage.cs
@@ -92,6 +92,53 @@ public GetNextRequestMessage(VersionCode version, int messageId, int requestId,
{
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The version.
+ /// The message id.
+ /// The request id.
+ /// Context name.
+ /// The variables.
+ /// The privacy provider.
+ /// Size of the max message.
+ public GetNextRequestMessage(VersionCode version, int messageId, int requestId, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize)
+ {
+ if (variables == null)
+ {
+ throw new ArgumentNullException(nameof(variables));
+ }
+
+ if (contextName == null)
+ {
+ throw new ArgumentNullException(nameof(contextName));
+ }
+
+ if (version != VersionCode.V3)
+ {
+ throw new ArgumentException("Only v3 is supported.", nameof(version));
+ }
+
+ if (privacy == null)
+ {
+ throw new ArgumentNullException(nameof(privacy));
+ }
+
+ Version = version;
+ Privacy = privacy;
+ Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable, new Integer32((int)SecurityModel.Tsm));
+ Parameters = SecurityParameters.Empty;
+
+ var pdu = new GetNextRequestPdu(
+ requestId,
+ variables);
+ var contextEngineId = OctetString.Empty;
+ Scope = new Scope(contextEngineId, contextName, pdu);
+
+ Privacy.ComputeHash(Version, Header, Parameters, Scope);
+ _bytes = this.PackMessage(null).ToBytes();
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -104,7 +151,8 @@ public GetNextRequestMessage(VersionCode version, int messageId, int requestId,
/// The privacy provider.
/// Size of the max message.
/// The report.
- public GetNextRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report)
+ /// The security model
+ public GetNextRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report, SecurityModel securityModel)
{
if (variables == null)
{
@@ -134,16 +182,25 @@ public GetNextRequestMessage(VersionCode version, int messageId, int requestId,
Version = version;
Privacy = privacy ?? throw new ArgumentNullException(nameof(privacy));
- Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable);
+ Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable, new Integer32((int)securityModel));
var parameters = report.Parameters;
var authenticationProvider = Privacy.AuthenticationProvider;
- Parameters = new SecurityParameters(
- parameters.EngineId,
- parameters.EngineBoots,
- parameters.EngineTime,
- userName,
- authenticationProvider.CleanDigest,
- Privacy.Salt);
+
+ if (securityModel == SecurityModel.Tsm)
+ {
+ Parameters = SecurityParameters.Empty;
+ }
+ else
+ {
+ Parameters = new SecurityParameters(
+ parameters.EngineId,
+ parameters.EngineBoots,
+ parameters.EngineTime,
+ userName,
+ authenticationProvider.CleanDigest,
+ Privacy.Salt);
+ }
+
var pdu = new GetNextRequestPdu(
requestId,
variables);
@@ -160,6 +217,22 @@ public GetNextRequestMessage(VersionCode version, int messageId, int requestId,
_bytes = this.PackMessage(null).ToBytes();
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The version.
+ /// The message id.
+ /// The request id.
+ /// Name of the user.
+ /// Context name.
+ /// The variables.
+ /// The privacy provider.
+ /// Size of the max message.
+ /// The report.
+ public GetNextRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report)
+ : this(version, messageId, requestId, userName, contextName, variables, privacy, maxMessageSize, report, SecurityModel.Usm)
+ {
+ }
///
/// Initializes a new instance of the class.
diff --git a/SharpSnmpLib/Messaging/GetRequestMessage.cs b/SharpSnmpLib/Messaging/GetRequestMessage.cs
index 590336f7..d7b7d9eb 100644
--- a/SharpSnmpLib/Messaging/GetRequestMessage.cs
+++ b/SharpSnmpLib/Messaging/GetRequestMessage.cs
@@ -83,6 +83,53 @@ public GetRequestMessage(VersionCode version, int messageId, int requestId, Octe
{
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The version.
+ /// The message id.
+ /// The request id.
+ /// Context name.
+ /// The variables.
+ /// The privacy provider.
+ /// Size of the max message.
+ public GetRequestMessage(VersionCode version, int messageId, int requestId, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize)
+ {
+ if (variables == null)
+ {
+ throw new ArgumentNullException(nameof(variables));
+ }
+
+ if (contextName == null)
+ {
+ throw new ArgumentNullException(nameof(contextName));
+ }
+
+ if (version != VersionCode.V3)
+ {
+ throw new ArgumentException("Only v3 is supported.", nameof(version));
+ }
+
+ if (privacy == null)
+ {
+ throw new ArgumentNullException(nameof(privacy));
+ }
+
+ Version = version;
+ Privacy = privacy;
+ Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable, new Integer32((int)SecurityModel.Tsm));
+ Parameters = SecurityParameters.Empty;
+
+ var pdu = new GetRequestPdu(
+ requestId,
+ variables);
+ var contextEngineId = OctetString.Empty;
+ Scope = new Scope(contextEngineId, contextName, pdu);
+
+ Privacy.ComputeHash(Version, Header, Parameters, Scope);
+ _bytes = this.PackMessage(null).ToBytes();
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -95,7 +142,8 @@ public GetRequestMessage(VersionCode version, int messageId, int requestId, Octe
/// The privacy provider.
/// Size of the max message.
/// The report.
- public GetRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report)
+ /// The type of security model
+ public GetRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report, SecurityModel securityModel)
{
if (userName == null)
{
@@ -125,16 +173,25 @@ public GetRequestMessage(VersionCode version, int messageId, int requestId, Octe
Version = version;
Privacy = privacy ?? throw new ArgumentNullException(nameof(privacy));
- Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable);
+ Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable, new Integer32((int)securityModel));
var parameters = report.Parameters;
var authenticationProvider = Privacy.AuthenticationProvider;
- Parameters = new SecurityParameters(
- parameters.EngineId,
- parameters.EngineBoots,
- parameters.EngineTime,
- userName,
- authenticationProvider.CleanDigest,
- Privacy.Salt);
+
+ if (securityModel == SecurityModel.Tsm)
+ {
+ Parameters = SecurityParameters.Empty;
+ }
+ else
+ {
+ Parameters = new SecurityParameters(
+ parameters.EngineId,
+ parameters.EngineBoots,
+ parameters.EngineTime,
+ userName,
+ authenticationProvider.CleanDigest,
+ Privacy.Salt);
+ }
+
var pdu = new GetRequestPdu(
requestId,
variables);
@@ -151,6 +208,22 @@ public GetRequestMessage(VersionCode version, int messageId, int requestId, Octe
_bytes = this.PackMessage(null).ToBytes();
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The version.
+ /// The message id.
+ /// The request id.
+ /// Name of the user.
+ /// Context name.
+ /// The variables.
+ /// The privacy provider.
+ /// Size of the max message.
+ /// The report.
+ public GetRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report)
+ : this(version, messageId, requestId, userName, contextName, variables, privacy, maxMessageSize, report, SecurityModel.Usm)
+ {
+ }
///
/// Initializes a new instance of the class.
diff --git a/SharpSnmpLib/Messaging/MessageFactory.cs b/SharpSnmpLib/Messaging/MessageFactory.cs
index d677456b..c8d513f9 100644
--- a/SharpSnmpLib/Messaging/MessageFactory.cs
+++ b/SharpSnmpLib/Messaging/MessageFactory.cs
@@ -142,14 +142,22 @@ private static ISnmpMessage ParseMessage(int first, Stream stream, UserRegistry
{
header = new Header(body[1]);
parameters = new SecurityParameters((OctetString)body[2]);
- var temp = registry.Find(parameters.UserName);
- if (temp == null)
+ if (header.SecurityModel == SecurityModel.Tsm)
{
- // handle decryption exception.
- return new MalformedMessage(header.MessageId, parameters.UserName, body[3]);
+ privacy = new TsmPrivacyProvider(TsmAuthenticationProvider.Instance);
+ }
+ else
+ {
+ var temp = registry.Find(parameters.UserName ?? OctetString.Empty);
+ if (temp == null)
+ {
+ // handle decryption exception.
+ return new MalformedMessage(header.MessageId, parameters.UserName ?? OctetString.Empty, body[3]);
+ }
+
+ privacy = temp;
}
- privacy = temp;
var code = body[3].TypeCode;
if (code == SnmpType.Sequence)
{
@@ -166,7 +174,7 @@ private static ISnmpMessage ParseMessage(int first, Stream stream, UserRegistry
catch (SnmpException)
{
// If decryption failed or gave back invalid data, handle parsing exceptions.
- return new MalformedMessage(header.MessageId, parameters.UserName, body[3]);
+ return new MalformedMessage(header.MessageId, parameters.UserName ?? OctetString.Empty, body[3]);
}
}
else
@@ -174,7 +182,7 @@ private static ISnmpMessage ParseMessage(int first, Stream stream, UserRegistry
throw new SnmpException(string.Format(CultureInfo.InvariantCulture, "invalid v3 packets scoped data: {0}", code));
}
- if (!privacy.VerifyHash(version, header, parameters, body[3], body.GetLengthBytes()))
+ if (header.SecurityModel != SecurityModel.Tsm && !privacy.VerifyHash(version, header, parameters, body[3], body.GetLengthBytes()))
{
parameters.IsInvalid = true;
}
diff --git a/SharpSnmpLib/Messaging/Net6SnmpMessageExtension.cs b/SharpSnmpLib/Messaging/Net6SnmpMessageExtension.cs
index 2a96a4d3..b5c8421c 100644
--- a/SharpSnmpLib/Messaging/Net6SnmpMessageExtension.cs
+++ b/SharpSnmpLib/Messaging/Net6SnmpMessageExtension.cs
@@ -202,8 +202,13 @@ public static async Task GetResponseAsync(this ISnmpMessage reques
}
var registry = new UserRegistry();
- if (request.Version == VersionCode.V3)
+ if (request.Version == VersionCode.V3 && request.Header.SecurityModel == SecurityModel.Usm)
{
+ if(request.Parameters.UserName is null)
+ {
+ throw new Exception($"{nameof(request)}.{nameof(request.Parameters)}.{nameof(request.Parameters.UserName)} cannot be null when using {SecurityModel.Usm}");
+ }
+
registry.Add(request.Parameters.UserName, request.Privacy);
}
diff --git a/SharpSnmpLib/Messaging/SecureDiscovery.cs b/SharpSnmpLib/Messaging/SecureDiscovery.cs
new file mode 100644
index 00000000..4456e15d
--- /dev/null
+++ b/SharpSnmpLib/Messaging/SecureDiscovery.cs
@@ -0,0 +1,175 @@
+// Discovery type.
+// Copyright (C) 2008-2010 Malcolm Crowe, Lex Li, and other contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+/*
+ * Created by SharpDevelop.
+ * User: lextm
+ * Date: 5/24/2009
+ * Time: 11:56 AM
+ *
+ * To change this template use Tools | Options | Coding | Edit Standard Headers.
+ */
+
+using System;
+using System.Globalization;
+using System.Net;
+using Lextm.SharpSnmpLib.Security;
+using System.Threading.Tasks;
+using DTLS;
+
+namespace Lextm.SharpSnmpLib.Messaging
+{
+ ///
+ /// Discovery class that participates in SNMP v3 discovery process.
+ ///
+ public sealed class SecureDiscovery
+ {
+ private readonly ISnmpMessage _discovery;
+ private static readonly UserRegistry Empty = new();
+ private static readonly SecurityParameters DefaultSecurityParameters =
+ new(
+ OctetString.Empty,
+ Integer32.Zero,
+ Integer32.Zero,
+ OctetString.Empty,
+ OctetString.Empty,
+ OctetString.Empty);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The request id.
+ /// The message id.
+ /// The max size of message.
+ public SecureDiscovery(int messageId, int requestId, int maxMessageSize)
+ {
+ _discovery = new GetRequestMessage(
+ VersionCode.V3,
+ new Header(
+ new Integer32(messageId),
+ new Integer32(maxMessageSize),
+ Levels.Reportable),
+ DefaultSecurityParameters,
+ new Scope(
+ OctetString.Empty,
+ OctetString.Empty,
+ new GetRequestPdu(requestId, [])),
+ DefaultPrivacyProvider.DefaultPair,
+ null);
+ }
+
+ ///
+ /// Gets the response.
+ ///
+ /// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
+ /// The receiver.
+ ///
+ #if NET6_0 || NET5_0
+ [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("GetResponse is incompatible with trimming.")]
+ #endif
+ public ReportMessage GetResponse(int timeout, IPEndPoint receiver)
+ {
+ if (receiver == null)
+ {
+ throw new ArgumentNullException(nameof(receiver));
+ }
+
+ using (var socket = receiver.GetSocket())
+ {
+ return (ReportMessage)_discovery.GetResponse(timeout, receiver, Empty, socket);
+ }
+ }
+
+ ///
+ /// Gets the response.
+ ///
+ /// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
+ /// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
+ /// The receiver.
+ /// The client for dtls.
+ public async Task GetResponseAsync(int connectionTimeout, int responseTimeout, IPEndPoint receiver, Client client)
+ {
+ if (receiver == null)
+ {
+ throw new ArgumentNullException(nameof(receiver));
+ }
+
+ return (ReportMessage)(await _discovery.GetSecureResponseAsync(TimeSpan.FromMilliseconds(connectionTimeout), TimeSpan.FromMilliseconds(responseTimeout), receiver, client, Empty).ConfigureAwait(false));
+ }
+
+ ///
+ /// Gets the response.
+ ///
+ /// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
+ /// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
+ /// The receiver.
+ /// The client for dtls.
+ ///
+ public async Task GetResponseAsync(TimeSpan connectionTimeout, TimeSpan responseTimeout, IPEndPoint receiver, Client client)
+ {
+ if (receiver == null)
+ {
+ throw new ArgumentNullException(nameof(receiver));
+ }
+
+ return (ReportMessage) (await _discovery.GetSecureResponseAsync(connectionTimeout, responseTimeout, receiver, client, Empty).ConfigureAwait(false));
+ }
+
+ ///
+ /// Gets the response.
+ ///
+ /// The receiver.
+ ///
+ #if NET6_0 || NET5_0
+ [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("GetResponseAsync is incompatible with trimming.")]
+ #endif
+ public async Task GetResponseAsync(IPEndPoint receiver)
+ {
+ if (receiver == null)
+ {
+ throw new ArgumentNullException(nameof(receiver));
+ }
+
+ using (var socket = receiver.GetSocket())
+ {
+ return (ReportMessage)await _discovery.GetResponseAsync(receiver, Empty, socket).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Converts to the bytes.
+ ///
+ ///
+ public byte[] ToBytes()
+ {
+ return _discovery.ToBytes();
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "discovery class: message id: {0}; request id: {1}", _discovery.MessageId(), _discovery.RequestId());
+ }
+ }
+}
diff --git a/SharpSnmpLib/Messaging/SecureMessageExtensions.cs b/SharpSnmpLib/Messaging/SecureMessageExtensions.cs
new file mode 100644
index 00000000..aed5f67c
--- /dev/null
+++ b/SharpSnmpLib/Messaging/SecureMessageExtensions.cs
@@ -0,0 +1,145 @@
+using DTLS;
+using Lextm.SharpSnmpLib.Security;
+using System;
+using System.Globalization;
+using System.Net;
+using System.Threading.Tasks;
+
+namespace Lextm.SharpSnmpLib.Messaging
+{
+ ///
+ /// Extension methods for .
+ ///
+ public static class SecureMessageExtensions
+ {
+ ///
+ /// Sends an and handles the response from agent.
+ ///
+ /// The .
+ /// The time-out value, in milliseconds for how long to wait for a connection
+ /// The time-out value, in milliseconds for how long to wait for a response. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
+ /// Agent.
+ /// The DTLS client
+ ///
+ public static async Task GetSecureResponseAsync(this ISnmpMessage request, TimeSpan connectionTimeout, TimeSpan responseTimeout, IPEndPoint receiver, Client client)
+ {
+ if (request is null)
+ {
+ throw new ArgumentNullException(nameof(request));
+ }
+
+ if (receiver is null)
+ {
+ throw new ArgumentNullException(nameof(receiver));
+ }
+
+ if (client is null)
+ {
+ throw new ArgumentNullException(nameof(client));
+ }
+
+ var registry = new UserRegistry();
+ //if (request.Version == VersionCode.V3)
+ //{
+ // registry.Add(request.Parameters.UserName, request.Privacy);
+ //}
+
+ return await request.GetSecureResponseAsync(connectionTimeout, responseTimeout, receiver, client, registry).ConfigureAwait(false);
+ }
+
+ ///
+ /// Sends an and handles the response from agent.
+ ///
+ /// The .
+ /// The time-out value, in milliseconds for how long to wait for a connection
+ /// The time-out value, in milliseconds for how long to wait for a response. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
+ /// Agent.
+ /// The DTLS client
+ ///
+ public static async Task GetSecureResponseAsync(this ISnmpMessage request, int connectionTimeout, int responseTimeout, IPEndPoint receiver, Client client)
+ {
+ if (request is null)
+ {
+ throw new ArgumentNullException(nameof(request));
+ }
+
+ if (receiver is null)
+ {
+ throw new ArgumentNullException(nameof(receiver));
+ }
+
+ if (client is null)
+ {
+ throw new ArgumentNullException(nameof(client));
+ }
+
+ var registry = new UserRegistry();
+ //if (request.Version == VersionCode.V3)
+ //{
+ // registry.Add(request.Parameters.UserName, request.Privacy);
+ //}
+
+ return await request.GetSecureResponseAsync(TimeSpan.FromMilliseconds(connectionTimeout), TimeSpan.FromMilliseconds(responseTimeout), receiver, client, registry).ConfigureAwait(false);
+ }
+
+ ///
+ /// Sends an and handles the response from agent.
+ ///
+ /// The .
+ /// The time-out value, in milliseconds for how long to wait for a connection
+ /// The time-out value, in milliseconds for how long to wait for a response. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
+ /// Agent.
+ /// The DTLS client
+ /// The user registry.
+ ///
+ public static async Task GetSecureResponseAsync(this ISnmpMessage request, TimeSpan connectionTimeout, TimeSpan responseTimeout, IPEndPoint receiver, Client client, UserRegistry registry)
+ {
+ if (request == null)
+ {
+ throw new ArgumentNullException(nameof(request));
+ }
+
+ if (receiver == null)
+ {
+ throw new ArgumentNullException(nameof(receiver));
+ }
+
+ if (client is null)
+ {
+ throw new ArgumentNullException(nameof(client));
+ }
+
+ if (registry == null)
+ {
+ throw new ArgumentNullException(nameof(registry));
+ }
+
+ var requestCode = request.TypeCode();
+ if (requestCode == SnmpType.TrapV1Pdu || requestCode == SnmpType.TrapV2Pdu || requestCode == SnmpType.ReportPdu)
+ {
+ throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "not a request message: {0}", requestCode));
+ }
+
+ var bytes = request.ToBytes();
+ await client.ConnectToServerAsync(receiver, responseTimeout, connectionTimeout).ConfigureAwait(false);
+ var reply = await client.SendAndGetResponseAsync(bytes, responseTimeout).ConfigureAwait(false);
+
+ // Passing 'count' is not necessary because ParseMessages should ignore it, but it offer extra safety (and would avoid an issue if parsing >1 response).
+ var response = MessageFactory.ParseMessages(reply, 0, reply.Length, registry)[0];
+ var responseCode = response.TypeCode();
+ if (responseCode == SnmpType.ResponsePdu || responseCode == SnmpType.ReportPdu)
+ {
+ var requestId = request.MessageId();
+ var responseId = response.MessageId();
+ if (responseId != requestId)
+ {
+ throw OperationException.Create(string.Format(CultureInfo.InvariantCulture, "wrong response sequence: expected {0}, received {1}", requestId, responseId), receiver.Address);
+ }
+
+ return response;
+ }
+
+ throw OperationException.Create(string.Format(CultureInfo.InvariantCulture, "wrong response type: {0}", responseCode), receiver.Address);
+ }
+ }
+}
diff --git a/SharpSnmpLib/Messaging/SetRequestMessage.cs b/SharpSnmpLib/Messaging/SetRequestMessage.cs
index 81ec6360..60844605 100644
--- a/SharpSnmpLib/Messaging/SetRequestMessage.cs
+++ b/SharpSnmpLib/Messaging/SetRequestMessage.cs
@@ -83,6 +83,48 @@ public SetRequestMessage(VersionCode version, int messageId, int requestId, Octe
{
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The version.
+ /// The message id.
+ /// The request id.
+ /// The context name.
+ /// The variables.
+ /// The privacy provider.
+ /// Size of the max message.
+ public SetRequestMessage(VersionCode version, int messageId, int requestId, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize)
+ {
+ if (variables == null)
+ {
+ throw new ArgumentNullException(nameof(variables));
+ }
+
+ if (contextName == null)
+ {
+ throw new ArgumentNullException(nameof(contextName));
+ }
+
+ if (version != VersionCode.V3)
+ {
+ throw new ArgumentException("Only v3 is supported.", nameof(version));
+ }
+
+ Version = version;
+ Privacy = privacy ?? throw new ArgumentNullException(nameof(privacy));
+ Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable, new Integer32((int)SecurityModel.Tsm));
+ Parameters = SecurityParameters.Empty;
+
+ var pdu = new SetRequestPdu(
+ requestId,
+ variables);
+ var contextEngineId = OctetString.Empty;
+ Scope = new Scope(contextEngineId, contextName, pdu);
+
+ Privacy.ComputeHash(Version, Header, Parameters, Scope);
+ _bytes = this.PackMessage(null).ToBytes();
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -95,7 +137,8 @@ public SetRequestMessage(VersionCode version, int messageId, int requestId, Octe
/// The privacy provider.
/// Size of the max message.
/// The report.
- public SetRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report)
+ /// The type of security model
+ public SetRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report, SecurityModel securityModel)
{
if (variables == null)
{
@@ -125,16 +168,25 @@ public SetRequestMessage(VersionCode version, int messageId, int requestId, Octe
Version = version;
Privacy = privacy ?? throw new ArgumentNullException(nameof(privacy));
- Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable);
+ Header = new Header(new Integer32(messageId), new Integer32(maxMessageSize), privacy.ToSecurityLevel() | Levels.Reportable, new Integer32((int)securityModel));
var parameters = report.Parameters;
var authenticationProvider = Privacy.AuthenticationProvider;
- Parameters = new SecurityParameters(
- parameters.EngineId,
- parameters.EngineBoots,
- parameters.EngineTime,
- userName,
- authenticationProvider.CleanDigest,
- Privacy.Salt);
+
+ if (securityModel == SecurityModel.Tsm)
+ {
+ Parameters = SecurityParameters.Empty;
+ }
+ else
+ {
+ Parameters = new SecurityParameters(
+ parameters.EngineId,
+ parameters.EngineBoots,
+ parameters.EngineTime,
+ userName,
+ authenticationProvider.CleanDigest,
+ Privacy.Salt);
+ }
+
var pdu = new SetRequestPdu(
requestId,
variables);
@@ -151,6 +203,24 @@ public SetRequestMessage(VersionCode version, int messageId, int requestId, Octe
_bytes = this.PackMessage(null).ToBytes();
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The version.
+ /// The message id.
+ /// The request id.
+ /// Name of the user.
+ /// The context name.
+ /// The variables.
+ /// The privacy provider.
+ /// Size of the max message.
+ /// The report.
+ public SetRequestMessage(VersionCode version, int messageId, int requestId, OctetString userName, OctetString contextName, IList variables, IPrivacyProvider privacy, int maxMessageSize, ISnmpMessage report)
+ : this(version, messageId, requestId, userName, contextName, variables, privacy, maxMessageSize, report, SecurityModel.Usm)
+ {
+
+ }
+
///
/// Initializes a new instance of the class.
///
diff --git a/SharpSnmpLib/Messaging/SnmpMessageExtension.cs b/SharpSnmpLib/Messaging/SnmpMessageExtension.cs
index 577ff07c..d82cabd8 100644
--- a/SharpSnmpLib/Messaging/SnmpMessageExtension.cs
+++ b/SharpSnmpLib/Messaging/SnmpMessageExtension.cs
@@ -121,7 +121,7 @@ public static OctetString Community(this ISnmpMessage message)
throw new ArgumentNullException(nameof(message));
}
- return message.Parameters.UserName;
+ return message.Parameters.UserName ?? OctetString.Empty;
}
#region sync methods
@@ -287,8 +287,13 @@ public static ISnmpMessage GetResponse(this ISnmpMessage request, int timeout, I
}
var registry = new UserRegistry();
- if (request.Version == VersionCode.V3)
+ if (request.Version == VersionCode.V3 && request.Header.SecurityModel == SecurityModel.Usm)
{
+ if (request.Parameters.UserName is null)
+ {
+ throw new Exception($"{nameof(request)}.{nameof(request.Parameters)}.{nameof(request.Parameters.UserName)} cannot be null when using {SecurityModel.Usm}");
+ }
+
registry.Add(request.Parameters.UserName, request.Privacy);
}
@@ -449,7 +454,7 @@ public static async Task SendAsync(this ISnmpMessage message, EndPoint manager,
}
var buffer = new ArraySegment(message.ToBytes());
- await socket.SendToAsync(buffer, SocketFlags.None, manager);
+ await socket.SendToAsync(buffer, SocketFlags.None, manager).ConfigureAwait(false);
}
///
@@ -544,8 +549,13 @@ public static async Task GetResponseAsync(this ISnmpMessage reques
}
var registry = new UserRegistry();
- if (request.Version == VersionCode.V3)
+ if (request.Version == VersionCode.V3 && request.Header.SecurityModel == SecurityModel.Usm)
{
+ if (request.Parameters.UserName is null)
+ {
+ throw new Exception($"{nameof(request)}.{nameof(request.Parameters)}.{nameof(request.Parameters.UserName)} cannot be null when using {SecurityModel.Usm}");
+ }
+
registry.Add(request.Parameters.UserName, request.Privacy);
}
@@ -591,7 +601,7 @@ public static async Task GetResponseAsync(this ISnmpMessage reques
// Whatever you change, try to keep the Send and the Receive close to each other.
var buffer = new ArraySegment(bytes);
- await udpSocket.SendToAsync(buffer, SocketFlags.None, receiver ?? throw new ArgumentNullException(nameof(receiver)));
+ await udpSocket.SendToAsync(buffer, SocketFlags.None, receiver ?? throw new ArgumentNullException(nameof(receiver))).ConfigureAwait(false);
int count;
byte[] reply = new byte[bufSize];
@@ -602,7 +612,7 @@ public static async Task GetResponseAsync(this ISnmpMessage reques
try
{
- var result = await udpSocket.ReceiveMessageFromAsync(new ArraySegment(reply), SocketFlags.None, remote);
+ var result = await udpSocket.ReceiveMessageFromAsync(new ArraySegment(reply), SocketFlags.None, remote).ConfigureAwait(false);
count = result.ReceivedBytes;
}
catch (SocketException ex)
diff --git a/SharpSnmpLib/Security/DESPrivacyProvider.cs b/SharpSnmpLib/Security/DESPrivacyProvider.cs
index f679e195..25045455 100644
--- a/SharpSnmpLib/Security/DESPrivacyProvider.cs
+++ b/SharpSnmpLib/Security/DESPrivacyProvider.cs
@@ -443,7 +443,7 @@ public ISnmpData Encrypt(ISnmpData data, SecurityParameters parameters)
throw new ArgumentNullException(nameof(parameters));
}
- if (data.TypeCode != SnmpType.Sequence && !(data is ISnmpPdu))
+ if (data.TypeCode != SnmpType.Sequence && data is not ISnmpPdu)
{
throw new ArgumentException("Invalid data type.", nameof(data));
}
diff --git a/SharpSnmpLib/Security/TsmAuthenticationProvider.cs b/SharpSnmpLib/Security/TsmAuthenticationProvider.cs
new file mode 100644
index 00000000..679101f0
--- /dev/null
+++ b/SharpSnmpLib/Security/TsmAuthenticationProvider.cs
@@ -0,0 +1,118 @@
+using System;
+
+namespace Lextm.SharpSnmpLib.Security
+{
+ ///
+ /// Tsm Authentication Provider
+ ///
+ public sealed class TsmAuthenticationProvider : IAuthenticationProvider
+ {
+ private TsmAuthenticationProvider()
+ {
+
+ }
+
+ private static readonly IAuthenticationProvider _Instance = new TsmAuthenticationProvider();
+
+ ///
+ /// instance of TsmAuthenticationProvider
+ ///
+ public static IAuthenticationProvider Instance => _Instance;
+
+ ///
+ /// Computes the hash.
+ ///
+ /// The version.
+ /// The header.
+ /// The parameters.
+ /// The scope data.
+ /// The privacy provider.
+ /// The length bytes.
+ ///
+ public OctetString ComputeHash(VersionCode version, ISegment header, SecurityParameters parameters, ISnmpData data, IPrivacyProvider privacy, byte[]? length)
+ {
+ if (header == null)
+ {
+ throw new ArgumentNullException(nameof(header));
+ }
+
+ if (parameters == null)
+ {
+ throw new ArgumentNullException(nameof(parameters));
+ }
+
+ if (data == null)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+
+ if (privacy == null)
+ {
+ throw new ArgumentNullException(nameof(privacy));
+ }
+
+ return OctetString.Empty;
+ }
+
+ ///
+ /// Computes the hash.
+ ///
+ ///
+ public static OctetString ComputeHash(byte[] buffer, OctetString engineId)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+ if (engineId == null)
+ {
+ throw new ArgumentNullException(nameof(engineId));
+ }
+
+ return OctetString.Empty;
+ }
+
+ ///
+ /// Gets the clean digest.
+ ///
+ /// The clean digest.
+ public OctetString CleanDigest => OctetString.Empty;
+
+ ///
+ /// Converts password to key.
+ ///
+ ///
+ ///
+ ///
+ public byte[] PasswordToKey(byte[] password, byte[] engineId)
+ {
+ if (password == null)
+ {
+ throw new ArgumentNullException(nameof(password));
+ }
+
+ if (engineId == null)
+ {
+ throw new ArgumentNullException(nameof(engineId));
+ }
+
+ // IMPORTANT: this function is not used.
+ return [];
+ }
+
+ ///
+ /// Gets the length of the digest.
+ ///
+ /// The length of the digest.
+ public int DigestLength => 0;
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString() => "TSM authentication provider";
+ }
+}
diff --git a/SharpSnmpLib/Security/TsmPrivacyProvider.cs b/SharpSnmpLib/Security/TsmPrivacyProvider.cs
new file mode 100644
index 00000000..db6b9b18
--- /dev/null
+++ b/SharpSnmpLib/Security/TsmPrivacyProvider.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+
+namespace Lextm.SharpSnmpLib.Security
+{
+ ///
+ /// Default privacy provider with default authentication provider.
+ ///
+ public sealed class TsmPrivacyProvider : IPrivacyProvider
+ {
+ private static readonly IPrivacyProvider _DefaultInstance = new DefaultPrivacyProvider(DefaultAuthenticationProvider.Instance);
+
+ ///
+ /// Default privacy provider with default authentication provider.
+ ///
+ public static IPrivacyProvider DefaultPair => _DefaultInstance;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Authentication provider.
+ public TsmPrivacyProvider(IAuthenticationProvider auth)
+ {
+ AuthenticationProvider = auth ?? throw new ArgumentNullException(nameof(auth));
+ }
+
+ #region IPrivacyProvider Members
+
+ ///
+ /// Corresponding .
+ ///
+ public IAuthenticationProvider AuthenticationProvider { get; private set; }
+
+ ///
+ /// Engine IDs.
+ ///
+ /// This is an optional field, and only used by TRAP v2 authentication.
+ public ICollection? EngineIds { get; set; }
+
+ ///
+ /// Decrypts the specified data.
+ ///
+ /// The data.
+ /// The parameters.
+ ///
+ public ISnmpData Decrypt(ISnmpData data, SecurityParameters parameters)
+ {
+ if (data == null)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+
+ if (parameters == null)
+ {
+ throw new ArgumentNullException(nameof(parameters));
+ }
+
+ if (data.TypeCode != SnmpType.Sequence)
+ {
+ var newException = new DecryptionException("Default decryption failed");
+ throw newException;
+ }
+
+ return data;
+ }
+
+ ///
+ /// Encrypts the specified scope.
+ ///
+ /// The scope data.
+ /// The parameters.
+ ///
+ public ISnmpData Encrypt(ISnmpData data, SecurityParameters parameters)
+ {
+ if (data == null)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+
+ if (parameters == null)
+ {
+ throw new ArgumentNullException(nameof(parameters));
+ }
+
+ if (data.TypeCode == SnmpType.Sequence || data is ISnmpPdu)
+ {
+ return data;
+ }
+
+ throw new ArgumentException("Invaild data type.", nameof(data));
+ }
+
+ ///
+ /// Gets the salt.
+ ///
+ /// The salt.
+ public OctetString Salt => OctetString.Empty;
+
+ ///
+ /// Passwords to key.
+ ///
+ /// The secret.
+ /// The engine identifier.
+ ///
+ public byte[] PasswordToKey(byte[] secret, byte[] engineId) => AuthenticationProvider.PasswordToKey(secret, engineId);
+
+ #endregion
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString() => "Tsm privacy provider";
+ }
+}
diff --git a/SharpSnmpLib/SecurityModel.cs b/SharpSnmpLib/SecurityModel.cs
new file mode 100644
index 00000000..eca4b229
--- /dev/null
+++ b/SharpSnmpLib/SecurityModel.cs
@@ -0,0 +1,36 @@
+// Security Model enum.
+// Copyright (C) 2008-2010 Malcolm Crowe, Lex Li, and other contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+namespace Lextm.SharpSnmpLib
+{
+ ///
+ /// Security Model enum for SNMPv3
+ ///
+ public enum SecurityModel
+ {
+ ///
+ /// USM - User-based Security Model
+ ///
+ Usm = 3,
+ ///
+ /// TSM - Transport Security Model
+ ///
+ Tsm = 4
+ }
+}
diff --git a/SharpSnmpLib/SecurityParameters.cs b/SharpSnmpLib/SecurityParameters.cs
index c7c85cd2..fb94d38b 100644
--- a/SharpSnmpLib/SecurityParameters.cs
+++ b/SharpSnmpLib/SecurityParameters.cs
@@ -36,6 +36,19 @@ namespace Lextm.SharpSnmpLib
///
public sealed class SecurityParameters : ISegment
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ private SecurityParameters()
+ {
+
+ }
+
+ ///
+ /// Initializes an empty instance of the class.
+ ///
+ public static SecurityParameters Empty => new();
+
///
/// Gets the engine ID.
///
@@ -58,7 +71,7 @@ public sealed class SecurityParameters : ISegment
/// Gets the user name.
///
/// The user name.
- public OctetString UserName { get; }
+ public OctetString? UserName { get; }
private OctetString? _authenticationParameters;
private readonly byte[]? _length;
@@ -112,6 +125,11 @@ public SecurityParameters(OctetString parameters)
throw new ArgumentNullException(nameof(parameters));
}
+ if (parameters == OctetString.Empty)
+ {
+ return;
+ }
+
var container = (Sequence)DataFactory.CreateSnmpData(parameters.GetRaw());
EngineId = (OctetString)container[0];
EngineBoots = (Integer32)container[1];
@@ -149,6 +167,10 @@ public SecurityParameters(OctetString? engineId, Integer32? engineBoots, Integer
///
public static SecurityParameters Create(OctetString userName)
{
+ if (userName is null)
+ {
+ throw new ArgumentNullException(nameof(userName));
+ }
return new SecurityParameters(null, null, null, userName, null, null);
}
@@ -170,7 +192,14 @@ public Sequence ToSequence()
///
public ISnmpData GetData(VersionCode version)
{
- return version == VersionCode.V3 ? new OctetString(ToSequence().ToBytes()) : UserName;
+ //if empty SecurityParameters, return an empty OctetString
+ if (_length == null && EngineId == null && EngineBoots == null && EngineTime == null && UserName == null && PrivacyParameters == null
+ && (AuthenticationParameters == OctetString.Empty || AuthenticationParameters == null))
+ {
+ return OctetString.Empty;
+ }
+
+ return version == VersionCode.V3 ? new OctetString(ToSequence().ToBytes()) : (UserName ?? OctetString.Empty);
}
#endregion
@@ -183,7 +212,7 @@ public ISnmpData GetData(VersionCode version)
///
public override string ToString()
{
- return string.Format(CultureInfo.InvariantCulture, "Security parameters: engineId: {0};engineBoots: {1};engineTime: {2};userName: {3}; authen hash: {4}; privacy hash: {5}", EngineId, EngineBoots, EngineTime, UserName, AuthenticationParameters == null ? null : AuthenticationParameters.ToHexString(), PrivacyParameters == null ? null : PrivacyParameters.ToHexString());
+ return string.Format(CultureInfo.InvariantCulture, "Security parameters: engineId: {0};engineBoots: {1};engineTime: {2};userName: {3}; authen hash: {4}; privacy hash: {5}", EngineId, EngineBoots, EngineTime, UserName, AuthenticationParameters?.ToHexString(), PrivacyParameters?.ToHexString());
}
///
diff --git a/SharpSnmpLib/SharpSnmpLib.csproj b/SharpSnmpLib/SharpSnmpLib.csproj
index bd61a8d1..962728be 100644
--- a/SharpSnmpLib/SharpSnmpLib.csproj
+++ b/SharpSnmpLib/SharpSnmpLib.csproj
@@ -40,6 +40,7 @@
S5547;S907;S1133;S101;S1135
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/Tests/CSharpCore/Unit/Messaging/GetRequestMessageTestFixture.cs b/Tests/CSharpCore/Unit/Messaging/GetRequestMessageTestFixture.cs
index 81248db3..03175ca4 100644
--- a/Tests/CSharpCore/Unit/Messaging/GetRequestMessageTestFixture.cs
+++ b/Tests/CSharpCore/Unit/Messaging/GetRequestMessageTestFixture.cs
@@ -109,6 +109,69 @@ public void TestConstructorV3Auth1()
Assert.Equal(ByteTool.Convert(bytes), request.ToBytes());
}
+ [Fact]
+ public void TestConstructorV3Auth2()
+ {
+ const string bytes = "30 73" +
+ "02 01 03 " +
+ "30 0F " +
+ "02 02 35 41 " +
+ "02 03 00 FF E3" +
+ "04 01 05" +
+ "02 01 03" +
+ "04 2E " +
+ "30 2C" +
+ "04 0D 80 00 1F 88 80 E9 63 00 00 D6 1F F4 49 " +
+ "02 01 0D " +
+ "02 01 57 " +
+ "04 05 6C 65 78 6C 69 " +
+ "04 0C 1C 6D 67 BF B2 38 ED 63 DF 0A 05 24 " +
+ "04 00 " +
+ "30 2D " +
+ "04 0D 80 00 1F 88 80 E9 63 00 00 D6 1F F4 49 " +
+ "04 00 " +
+ "A0 1A 02 02 01 AF 02 01 00 02 01 00 30 0E 30 0C 06 08 2B 06 01 02 01 01 03 00 05 00";
+ ReportMessage report = new ReportMessage(
+ VersionCode.V3,
+ new Header(
+ new Integer32(13633),
+ new Integer32(0xFFE3),
+ 0),
+ new SecurityParameters(
+ new OctetString(ByteTool.Convert("80 00 1F 88 80 E9 63 00 00 D6 1F F4 49")),
+ new Integer32(0x0d),
+ new Integer32(0x57),
+ new OctetString("lexli"),
+ new OctetString(new byte[12]),
+ OctetString.Empty),
+ new Scope(
+ new OctetString(ByteTool.Convert("80 00 1F 88 80 E9 63 00 00 D6 1F F4 49")),
+ OctetString.Empty,
+ new ReportPdu(
+ 0x01AF,
+ ErrorCode.NoError,
+ 0,
+ new List(1) { new Variable(new ObjectIdentifier("1.3.6.1.2.1.1.3.0")) })),
+ DefaultPrivacyProvider.DefaultPair,
+ null);
+
+ IPrivacyProvider privacy = new DefaultPrivacyProvider(new MD5AuthenticationProvider(new OctetString("testpass")));
+ GetRequestMessage request = new GetRequestMessage(
+ VersionCode.V3,
+ 13633,
+ 0x01AF,
+ new OctetString("lexli"),
+ OctetString.Empty,
+ new List(1) { new Variable(new ObjectIdentifier("1.3.6.1.2.1.1.3.0")) },
+ privacy,
+ Messenger.MaxMessageSize,
+ report,
+ SecurityModel.Usm);
+
+ Assert.Equal(Levels.Authentication | Levels.Reportable, request.Header.SecurityLevel);
+ Assert.Equal(ByteTool.Convert(bytes), request.ToBytes());
+ }
+
[Fact]
public void TestConstructorV2AuthMd5PrivDes()
{
@@ -238,6 +301,27 @@ public void TestConstructorV3AuthSha()
Assert.Equal(ByteTool.Convert(bytes), request.ToBytes());
}
+ [Fact]
+ public void TestConstructorV3OverDtls()
+ {
+ const string bytes = "30 3c 02 01 03 30 11 02 04 2f 1f 26 a3 02 03 00" +
+ "ff e3 04 01 07 02 01 04 04 00 30 22 04 00 04 00" +
+ "a0 1c 02 04 ab 53 bd 3f 02 01 00 02 01 00 30 0e" +
+ "30 0c 06 08 2b 06 01 02 01 01 03 00 05 00";
+
+ var request = new GetRequestMessage(
+ VersionCode.V3,
+ 790570659,
+ -1420575425,
+ OctetString.Empty,
+ new List() { new Variable(new ObjectIdentifier("1.3.6.1.2.1.1.3.0")) },
+ new TsmPrivacyProvider(TsmAuthenticationProvider.Instance),
+ 65507);
+
+ Assert.Equal(Levels.Authentication | Levels.Reportable | Levels.Privacy, request.Header.SecurityLevel);
+ Assert.Equal(ByteTool.Convert(bytes), request.ToBytes());
+ }
+
[Fact]
public void TestDiscoveryV3()
{
diff --git a/Tests/CSharpCore/Unit/Security/TsmAuthenticationProviderTestFixture.cs b/Tests/CSharpCore/Unit/Security/TsmAuthenticationProviderTestFixture.cs
new file mode 100644
index 00000000..9ad3e072
--- /dev/null
+++ b/Tests/CSharpCore/Unit/Security/TsmAuthenticationProviderTestFixture.cs
@@ -0,0 +1,19 @@
+using System;
+using Lextm.SharpSnmpLib.Security;
+using Xunit;
+
+namespace Lextm.SharpSnmpLib.Unit.Security
+{
+ public class TsmAuthenticationProviderTestFixture
+ {
+ [Fact]
+ public void Test()
+ {
+ var provider = TsmAuthenticationProvider.Instance;
+ Assert.Equal("TSM authentication provider", provider.ToString());
+ Assert.Throws(() => provider.PasswordToKey(null, null));
+ Assert.Throws(() => provider.PasswordToKey(new byte[0], null));
+ Assert.Equal(new byte[0], provider.PasswordToKey(new byte[0], new byte[0]));
+ }
+ }
+}
diff --git a/Tests/CSharpCore/Unit/Security/TsmPrivacyProviderTestFixture.cs b/Tests/CSharpCore/Unit/Security/TsmPrivacyProviderTestFixture.cs
new file mode 100644
index 00000000..9cfc9aca
--- /dev/null
+++ b/Tests/CSharpCore/Unit/Security/TsmPrivacyProviderTestFixture.cs
@@ -0,0 +1,26 @@
+using System;
+using Lextm.SharpSnmpLib.Security;
+using Xunit;
+
+namespace Lextm.SharpSnmpLib.Unit.Security
+{
+ public class TsmPrivacyProviderTestFixture
+ {
+ [Fact]
+ public void Test()
+ {
+ var provider = TsmPrivacyProvider.DefaultPair;
+ Assert.Throws(() => provider.Encrypt(null, null));
+ Assert.Throws(() => provider.Encrypt(OctetString.Empty, null));
+ Assert.Throws(() => provider.Encrypt(new Null(), SecurityParameters.Create(OctetString.Empty)));
+
+ var expected = new Sequence((byte[])null);
+ Assert.Equal(expected, provider.Encrypt(expected, SecurityParameters.Create(OctetString.Empty)));
+
+ Assert.Throws(() => provider.Decrypt(null, null));
+ Assert.Throws(() => provider.Decrypt(OctetString.Empty, null));
+ var result = provider.Decrypt(new Sequence((byte[])null), SecurityParameters.Create(OctetString.Empty));
+ Assert.NotNull(result);
+ }
+ }
+}
diff --git a/Tests/CSharpCore/Unit/SecurityParametersTestFixture.cs b/Tests/CSharpCore/Unit/SecurityParametersTestFixture.cs
index 89862c04..7d06d1e0 100644
--- a/Tests/CSharpCore/Unit/SecurityParametersTestFixture.cs
+++ b/Tests/CSharpCore/Unit/SecurityParametersTestFixture.cs
@@ -21,5 +21,11 @@ public void TestToString()
obj.AuthenticationParameters = new OctetString("one");
Assert.Throws(() => obj.AuthenticationParameters = new OctetString("me"));
}
+
+ [Fact]
+ public void TestGetData()
+ {
+ Assert.Equal(OctetString.Empty, SecurityParameters.Empty.GetData(VersionCode.V3));
+ }
}
}