ln.snmp/endpoint/USMEndpoint.cs

238 lines
7.7 KiB
C#

using System;
using System.Net;
using ln.snmp.types;
using System.Text;
using System.Security.Cryptography;
using ln.snmp.asn1;
using System.Linq;
using ln.logging;
namespace ln.snmp.endpoint
{
public enum SnmpV3AuthMethod { MD5, SHA }
public enum SnmpV3PrivMethod { DES }
public enum SnmpAuthLevel : int { noAuthNoPriv = 0, authNoPriv = 0x01, authPriv = 0x03 }
public class USMEndpoint : SnmpEndpoint
{
public OctetString RemoteEngineID { get; set; }
public Integer CacheAuthoritativeEngineBoots { get; set; }
public Integer CacheAuthoritativeEngineTime { get; set; }
public string Username { get; set; }
public byte[] AuthKey { get; set; }
public byte[] PrivKey { get; set; }
public byte[] LocalAuthKey { get; set; }
public byte[] LocalPrivKey { get; set; }
public SnmpV3AuthMethod AuthMethod { get; set; }
public SnmpV3PrivMethod PrivMethod { get; set; }
public override SnmpVersion SnmpVersion => SnmpVersion.V3;
public USMEndpoint(SNMPEngine snmpEngine,IPEndPoint remoteEndpoint)
:base(snmpEngine,remoteEndpoint)
{
}
public SnmpAuthLevel AuthLevel
{
get
{
if (PrivKey != null)
return SnmpAuthLevel.authPriv;
if (AuthKey != null)
return SnmpAuthLevel.authNoPriv;
return SnmpAuthLevel.noAuthNoPriv;
}
}
public override PDU snmpRequest(PDU pdu)
{
if ((RemoteEngineID == null)||(RemoteEngineID.Bytes.Length == 0))
{
QueryEngineID();
}
USMMessage request = new USMMessage();
ScopedPDU scopedPDU = new ScopedPDU();
request.MessageID = SNMPEngine.NextMessageID;
request.msgData = scopedPDU;
scopedPDU.contextEngineID = RemoteEngineID;
scopedPDU.PDU = pdu;
AuthenticateMessage(request);
USMMessage replyUSM = InternalRequest(request);
PDU responsePDU = (replyUSM.msgData as ScopedPDU).PDU;
return responsePDU;
}
private USMMessage InternalRequest(USMMessage request)
{
SnmpMessage reply = SNMPEngine.SNMPRequest(RemoteEndpoint, request, SNMPEngine.Timeout);
USMMessage usmReply = reply as USMMessage;
CacheAuthoritativeEngineBoots = usmReply.SecurityParameters.msgAuthoritativeEngineBoots;
CacheAuthoritativeEngineTime = usmReply.SecurityParameters.msgAuthoritativeEngineTime;
return usmReply;
}
public void QueryEngineID()
{
USMMessage queryMessage = new USMMessage();
queryMessage.MessageID = SNMPEngine.NextMessageID;
ScopedPDU scopedPDU = new ScopedPDU();
queryMessage.msgData = scopedPDU;
queryMessage.Dump();
USMMessage usmReply = InternalRequest(queryMessage);
RemoteEngineID = usmReply.SecurityParameters.msgAuthoritativeEngineID;
//RemoteEngineID.Bytes = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 };
LocalizeKeys();
}
public bool AuthenticateMessage(USMMessage message)
{
message.msgGlobalData.msgFlags.Bytes = new byte[] { (byte)(int)AuthLevel };
message.SecurityParameters.msgUserName.StringValue = Username;
message.SecurityParameters.msgAuthoritativeEngineID = RemoteEngineID;
message.SecurityParameters.msgAuthenticationParameters.Bytes = new byte[12];
message.SecurityParameters.msgAuthoritativeEngineBoots = CacheAuthoritativeEngineBoots != null ? CacheAuthoritativeEngineBoots : new Integer();
message.SecurityParameters.msgAuthoritativeEngineTime = CacheAuthoritativeEngineTime != null ? CacheAuthoritativeEngineTime : new Integer();
byte[] wholeMsg = ((ASN1Value)message).AsByteArray;
byte[] extendedAuthKey = new byte[64];
byte[] authKey = LocalAuthKey;
Array.Copy(authKey, extendedAuthKey, authKey.Length);
byte[] K1 = new byte[64];
for (int n = 0; n < 64; n++)
K1[n] = (byte)(extendedAuthKey[n] ^ IPAD[n]);
byte[] K2 = new byte[64];
for (int n = 0; n < 64; n++)
K2[n] = (byte)(extendedAuthKey[n] ^ OPAD[n]);
using (HashAlgorithm hash = CreateAuthHashAlgorithm())
{
byte[] b1 = new byte[K1.Length + wholeMsg.Length];
Array.Copy(K1, b1, K1.Length);
Array.Copy(wholeMsg, 0, b1, K1.Length, wholeMsg.Length);
byte[] intermediate = hash.ComputeHash(b1);
byte[] inter2 = new byte[K2.Length + intermediate.Length];
Array.Copy(K2, inter2, K2.Length);
Array.Copy(intermediate, 0, inter2, K2.Length, intermediate.Length);
byte[] mac = hash.ComputeHash(inter2);
message.SecurityParameters.msgAuthenticationParameters.Bytes = mac.Take(12).ToArray();
}
return true;
}
private byte[] DeriveKey(string passphrase,HashAlgorithm hash)
{
using (hash)
{
byte[] pass = Encoding.ASCII.GetBytes(passphrase);
byte[] block = new byte[64];
for (int n = 0; n < 1048576; n += 64)
{
for (int p = 0; p < block.Length; p++)
{
block[p] = pass[(p + n) % pass.Length];
}
hash.TransformBlock(block, 0, block.Length, block, 0);
}
hash.TransformFinalBlock(new byte[0], 0, 0);
byte[] result = hash.Hash;
return result;
}
}
private HashAlgorithm CreateAuthHashAlgorithm()
{
return AuthMethod == SnmpV3AuthMethod.SHA ? (HashAlgorithm)SHA1.Create() : (HashAlgorithm)MD5.Create();
}
private void LocalizeKeys()
{
if ((AuthKey != null) && (RemoteEngineID != null))
{
using (HashAlgorithm hash = CreateAuthHashAlgorithm())
{
byte[] engineID = (byte[])RemoteEngineID.Bytes.Clone();
hash.TransformBlock(AuthKey, 0, AuthKey.Length, AuthKey, 0);
hash.TransformBlock(engineID, 0, engineID.Length, engineID, 0);
hash.TransformBlock(AuthKey, 0, AuthKey.Length, AuthKey, 0);
hash.TransformFinalBlock(new byte[0], 0, 0);
LocalAuthKey = hash.Hash;
Logging.Log(LogLevel.DEBUG, "Derived LocalAuthKey from AuthKey: {0}", BitConverter.ToString(LocalAuthKey));
}
}
}
public string AuthKeyPhrase
{
get => throw new NotSupportedException();
set
{
AuthKey = DeriveKey(
value,
CreateAuthHashAlgorithm()
);
Logging.Log(LogLevel.DEBUG, "Derived AuthKey from Phrase: {0}",BitConverter.ToString(AuthKey));
if (RemoteEngineID != null)
LocalizeKeys();
}
}
public string PrivKeyPhrase { get; set; }
static byte[] IPAD = __PAD(0x36);
static byte[] OPAD = __PAD(0x5C);
public static byte[] __PAD(byte pad)
{
byte[] ipad = new byte[0x40];
for (int n = 0; n < ipad.Length; n++)
ipad[n] = pad;
return ipad;
}
}
}