using System; using System.Diagnostics; using System.IO; using System.Net; using System.Security.Cryptography; using DnDns.Enums; using DnDns.Query; using DnDns.Records; namespace DnDns.Security { /// /// Implements TSIG signing of DNS messages as per RFC2845 /// /// This class only supports the one hashing algorithim, hmac-sha256. /// It would be trivial to add more. public class TsigMessageSecurityProvider : IMessageSecurityProvider { private const string Hmacsha1String = "hmac-sha256."; private readonly string _name; private readonly string _algorithimName; private readonly ushort _fudge; private readonly byte[] _sharedKey; private readonly HMACSHA256 _hmac; /// /// Initalise the /// /// The name of the shared key /// The shared key in base64 string format /// The signing time fudge value public TsigMessageSecurityProvider(string name, string sharedKey, ushort fudge) { _name = name; _fudge = fudge; _sharedKey = Convert.FromBase64String(sharedKey); if (_sharedKey == null) { throw new ArgumentException("Argument is not a valid base64 string", "sharedKey"); } _hmac = new HMACSHA256(_sharedKey); _algorithimName = Hmacsha1String; } #region IMessageSecurityProvider Members /// /// Apply a TSIG record to the request message. /// /// The to add the security headers too. /// A instance with additional security attributes assigned. public DnsQueryRequest SecureMessage(DnsQueryRequest dnsQueryRequest) { DateTime signDateTime = DateTime.Now; int timeHigh; long timeLow; byte[] messageBytes = dnsQueryRequest.GetMessageBytes(); Trace.WriteLine(String.Format("Message Header Bytes: {0}", DnsHelpers.DumpArrayToString(messageBytes))); MemoryStream memoryStream = new MemoryStream(); memoryStream.Write(messageBytes, 0, messageBytes.Length); // the shared key name byte[] data = DnsHelpers.CanonicaliseDnsName(_name, false); memoryStream.Write(data, 0, data.Length); data = BitConverter.GetBytes((ushort) (IPAddress.HostToNetworkOrder((ushort) NsClass.ANY) >> 16)); memoryStream.Write(data, 0, data.Length); // the TTL value data = BitConverter.GetBytes((uint) (IPAddress.HostToNetworkOrder((uint) 0) >> 32)); memoryStream.Write(data, 0, data.Length); // the algorithim name data = DnsHelpers.CanonicaliseDnsName(_algorithimName, true); memoryStream.Write(data, 0, data.Length); DnsHelpers.ConvertToDnsTime(signDateTime.ToUniversalTime(), out timeHigh, out timeLow); data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)timeHigh) >> 16)); memoryStream.Write(data, 0, data.Length); data = BitConverter.GetBytes((uint) (IPAddress.HostToNetworkOrder((uint)timeLow) >> 32)); memoryStream.Write(data, 0, data.Length); data = BitConverter.GetBytes((ushort) (IPAddress.HostToNetworkOrder(_fudge) >> 16)); memoryStream.Write(data, 0, data.Length); data = BitConverter.GetBytes((ushort) (IPAddress.HostToNetworkOrder((ushort) RCode.NoError) >> 16)); memoryStream.Write(data, 0, data.Length); // no other data data = BitConverter.GetBytes((ushort) (IPAddress.HostToNetworkOrder((ushort) 0) >> 16)); memoryStream.Write(data, 0, data.Length); byte[] dataToHash = memoryStream.ToArray(); Trace.WriteLine(String.Format("Data to hash: {0}", DnsHelpers.DumpArrayToString(dataToHash))); byte[] mac = _hmac.ComputeHash(dataToHash); Trace.WriteLine(String.Format("hash: {0}", DnsHelpers.DumpArrayToString(mac))); dnsQueryRequest.AdditionalRRecords.Add(new TSigRecord(_name, _algorithimName, RCode.NoError, _fudge, dnsQueryRequest.TransactionID, new byte[] { }, mac, signDateTime)); return dnsQueryRequest; } #endregion } }