internal byte[] GetNSec3Hash(NSec3HashAlgorithm algorithm, int iterations, byte[] salt) { IDigest digest; switch (algorithm) { case NSec3HashAlgorithm.Sha1: digest = new Sha1Digest(); break; default: throw new NotSupportedException(); } byte[] buffer = new byte[Math.Max(MaximumRecordDataLength + 1, digest.GetDigestSize()) + salt.Length]; int length = 0; DnsMessageBase.EncodeDomainName(buffer, 0, ref length, this, null, true); for (int i = 0; i <= iterations; i++) { DnsMessageBase.EncodeByteArray(buffer, ref length, salt); digest.BlockUpdate(buffer, 0, length); digest.DoFinal(buffer, 0); length = digest.GetDigestSize(); } byte[] res = new byte[length]; Buffer.BlockCopy(buffer, 0, res, 0, length); return(res); }
/// <summary> /// Creates a new instance of the NSec3ParamRecord class /// </summary> /// <param name="name"> Name of the record </param> /// <param name="recordClass"> Class of the record </param> /// <param name="timeToLive"> Seconds the record should be cached at most </param> /// <param name="hashAlgorithm"> Algorithm of hash </param> /// <param name="flags"> Flags of the record </param> /// <param name="iterations"> Number of iterations </param> /// <param name="salt"> Binary data of salt </param> public NSec3ParamRecord(DomainName name, RecordClass recordClass, int timeToLive, NSec3HashAlgorithm hashAlgorithm, byte flags, ushort iterations, byte[] salt) : base(name, RecordType.NSec3Param, recordClass, timeToLive) { HashAlgorithm = hashAlgorithm; Flags = flags; Iterations = iterations; Salt = salt ?? new byte[] { }; }
public static int GetPriority(this NSec3HashAlgorithm algorithm) { switch (algorithm) { case NSec3HashAlgorithm.Sha1: return(1); default: throw new NotSupportedException(); } }
public static bool IsSupported(this NSec3HashAlgorithm algorithm) { switch (algorithm) { case NSec3HashAlgorithm.Sha1: return(true); default: return(false); } }
/// <summary> /// Creates of new instance of the NSec3Record class /// </summary> /// <param name="name"> Name of the record </param> /// <param name="recordClass"> Class of the record </param> /// <param name="timeToLive"> Seconds the record should be cached at most </param> /// <param name="hashAlgorithm"> Algorithm of hash </param> /// <param name="flags"> Flags of the record </param> /// <param name="iterations"> Number of iterations </param> /// <param name="salt"> Binary data of salt </param> /// <param name="nextHashedOwnerName"> Binary data of hash of next owner </param> /// <param name="types"> Types of next owner </param> public NSec3Record(DomainName name, RecordClass recordClass, int timeToLive, NSec3HashAlgorithm hashAlgorithm, byte flags, ushort iterations, byte[] salt, byte[] nextHashedOwnerName, List <RecordType> types) : base(name, RecordType.NSec3, recordClass, timeToLive) { HashAlgorithm = hashAlgorithm; Flags = flags; Iterations = iterations; Salt = salt ?? new byte[] { }; NextHashedOwnerName = nextHashedOwnerName ?? new byte[] { }; if ((types == null) || (types.Count == 0)) { Types = new List <RecordType>(); } else { Types = types.Distinct().OrderBy(x => x).ToList(); } }
/// <summary> /// Creates of new instance of the NSec3Record class /// </summary> /// <param name="name"> Name of the record </param> /// <param name="recordClass"> Class of the record </param> /// <param name="timeToLive"> Seconds the record should be cached at most </param> /// <param name="hashAlgorithm"> Algorithm of hash </param> /// <param name="flags"> Flags of the record </param> /// <param name="iterations"> Number of iterations </param> /// <param name="salt"> Binary data of salt </param> /// <param name="nextHashedOwnerName"> Binary data of hash of next owner </param> /// <param name="types"> Types of next owner </param> public NSec3Record(DomainName name, RecordClass recordClass, int timeToLive, NSec3HashAlgorithm hashAlgorithm, byte flags, ushort iterations, byte[] salt, byte[] nextHashedOwnerName, List<RecordType> types) : base(name, RecordType.NSec3, recordClass, timeToLive) { HashAlgorithm = hashAlgorithm; Flags = flags; Iterations = iterations; Salt = salt ?? new byte[] { }; NextHashedOwnerName = nextHashedOwnerName ?? new byte[] { }; if ((types == null) || (types.Count == 0)) { Types = new List<RecordType>(); } else { Types = types.Distinct().OrderBy(x => x).ToList(); } }
private Zone SignWithNSec3(DateTime inception, DateTime expiration, List <DnsKeyRecord> zoneSigningKeys, List <DnsKeyRecord> keySigningKeys, NSec3HashAlgorithm nsec3Algorithm, int nsec3Iterations, byte[] nsec3Salt, bool nsec3OptOut) { var soaRecord = _records.OfType <SoaRecord>().First(); var subZoneNameserver = _records.Where(x => (x.RecordType == RecordType.Ns) && (x.Name != Name)).ToList(); var subZones = subZoneNameserver.Select(x => x.Name).Distinct().ToList(); var unsignedRecords = _records.Where(x => subZones.Any(y => x.Name.IsSubDomainOf(y))).ToList(); // glue records if (nsec3OptOut) { unsignedRecords = unsignedRecords.Union(subZoneNameserver.Where(x => !_records.Any(y => (y.RecordType == RecordType.Ds) && (y.Name == x.Name)))).ToList(); // delegations without DS record } var recordsByName = _records.Except(unsignedRecords).Union(zoneSigningKeys).Union(keySigningKeys).GroupBy(x => x.Name).Select(x => new Tuple <DomainName, List <DnsRecordBase> >(x.Key, x.OrderBy(y => y.RecordType == RecordType.Soa ? -1 : (int)y.RecordType).ToList())).OrderBy(x => x.Item1).ToList(); byte nsec3RecordFlags = (byte)(nsec3OptOut ? 1 : 0); Zone res = new Zone(Name, Count * 3); List <NSec3Record> nSec3Records = new List <NSec3Record>(Count); if (nsec3Salt == null) { nsec3Salt = SecureRandom.GetSeed(8); } recordsByName[0].Item2.Add(new NSec3ParamRecord(soaRecord.Name, soaRecord.RecordClass, 0, nsec3Algorithm, 0, (ushort)nsec3Iterations, nsec3Salt)); HashSet <DomainName> allNames = new HashSet <DomainName>(); for (int i = 0; i < recordsByName.Count; i++) { List <RecordType> recordTypes = new List <RecordType>(); DomainName currentName = recordsByName[i].Item1; foreach (var recordsByType in recordsByName[i].Item2.GroupBy(x => x.RecordType)) { List <DnsRecordBase> records = recordsByType.ToList(); recordTypes.Add(recordsByType.Key); res.AddRange(records); // do not sign nameserver delegations for sub zones if ((records[0].RecordType == RecordType.Ns) && (currentName != Name)) { continue; } recordTypes.Add(RecordType.RrSig); foreach (var key in zoneSigningKeys) { res.Add(new RrSigRecord(records, key, inception, expiration)); } if (records[0].RecordType == RecordType.DnsKey) { foreach (var key in keySigningKeys) { res.Add(new RrSigRecord(records, key, inception, expiration)); } } } byte[] hash = recordsByName[i].Item1.GetNSec3Hash(nsec3Algorithm, nsec3Iterations, nsec3Salt); nSec3Records.Add(new NSec3Record(DomainName.ParseFromMasterfile(hash.ToBase32HexString()) + Name, soaRecord.RecordClass, soaRecord.NegativeCachingTTL, nsec3Algorithm, nsec3RecordFlags, (ushort)nsec3Iterations, nsec3Salt, hash, recordTypes)); allNames.Add(currentName); for (int j = currentName.LabelCount - Name.LabelCount; j > 0; j--) { DomainName possibleNonTerminal = currentName.GetParentName(j); if (!allNames.Contains(possibleNonTerminal)) { hash = possibleNonTerminal.GetNSec3Hash(nsec3Algorithm, nsec3Iterations, nsec3Salt); nSec3Records.Add(new NSec3Record(DomainName.ParseFromMasterfile(hash.ToBase32HexString()) + Name, soaRecord.RecordClass, soaRecord.NegativeCachingTTL, nsec3Algorithm, nsec3RecordFlags, (ushort)nsec3Iterations, nsec3Salt, hash, new List <RecordType>())); allNames.Add(possibleNonTerminal); } } } nSec3Records = nSec3Records.OrderBy(x => x.Name).ToList(); byte[] firstNextHashedOwnerName = nSec3Records[0].NextHashedOwnerName; for (int i = 1; i < nSec3Records.Count; i++) { nSec3Records[i - 1].NextHashedOwnerName = nSec3Records[i].NextHashedOwnerName; } nSec3Records[nSec3Records.Count - 1].NextHashedOwnerName = firstNextHashedOwnerName; foreach (var nSec3Record in nSec3Records) { res.Add(nSec3Record); foreach (var key in zoneSigningKeys) { res.Add(new RrSigRecord(new List <DnsRecordBase>() { nSec3Record }, key, inception, expiration)); } } res.AddRange(unsignedRecords); return(res); }
/// <summary> /// Signs a zone /// </summary> /// <param name="keys">A list of keys to sign the zone</param> /// <param name="inception">The inception date of the signatures</param> /// <param name="expiration">The expiration date of the signatures</param> /// <param name="nsec3Algorithm">The NSEC3 algorithm (or 0 when NSEC should be used)</param> /// <param name="nsec3Iterations">The number of iterations when NSEC3 is used</param> /// <param name="nsec3Salt">The salt when NSEC3 is used</param> /// <param name="nsec3OptOut">true, of NSEC3 OptOut should be used for delegations without DS record</param> /// <returns>A signed zone</returns> public Zone Sign(List <DnsKeyRecord> keys, DateTime inception, DateTime expiration, NSec3HashAlgorithm nsec3Algorithm = 0, int nsec3Iterations = 10, byte[] nsec3Salt = null, bool nsec3OptOut = false) { if ((keys == null) || (keys.Count == 0)) { throw new Exception("No DNS Keys were provided"); } if (!keys.All(x => x.IsZoneKey)) { throw new Exception("No DNS key with Zone Key Flag were provided"); } if (keys.Any(x => (x.PrivateKey == null) || (x.PrivateKey.Length == 0))) { throw new Exception("For at least one DNS key no Private Key was provided"); } if (keys.Any(x => (x.Protocol != 3) || ((nsec3Algorithm != 0) ? !x.Algorithm.IsCompatibleWithNSec3() : !x.Algorithm.IsCompatibleWithNSec()))) { throw new Exception("At least one invalid DNS key was provided"); } List <DnsKeyRecord> keySigningKeys = keys.Where(x => x.IsSecureEntryPoint).ToList(); List <DnsKeyRecord> zoneSigningKeys = keys.Where(x => !x.IsSecureEntryPoint).ToList(); if (nsec3Algorithm == 0) { return(SignWithNSec(inception, expiration, zoneSigningKeys, keySigningKeys)); } else { return(SignWithNSec3(inception, expiration, zoneSigningKeys, keySigningKeys, nsec3Algorithm, nsec3Iterations, nsec3Salt, nsec3OptOut)); } }
internal DomainName GetNsec3HashName(NSec3HashAlgorithm algorithm, int iterations, byte[] salt, DomainName zoneApex) { return(new DomainName(GetNSec3Hash(algorithm, iterations, salt).ToBase32HexString(), zoneApex)); }