Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions APIDOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2673,13 +2673,13 @@ WHERE:
- `token`: The session token generated by the `login` or the `createToken` call.
- `node` (optional): The node domain name for which the this API call is intended. When unspecified, the current node is used. This parameter can be used only when Clustering is initialized.
- `zone`: The name of the primary zone to sign.
- `algorithm`: The algorithm to be used for signing. Valid values are [`RSA`, `ECDSA`, `EDDSA`].
- `algorithm`: The algorithm to be used for signing. Valid values are [`RSA`, `ECDSA`, `EDDSA`, `MLDSA`].
- `pemKskPrivateKey` (optional): The user specified private key in PEM format for Key Signing Key (KSK). When this parameter is specified, the private key specified is used instead of automatically generating it.
- `pemZskPrivateKey` (optional): The user specified private key in PEM format for Zone Signing Key (ZSK). When this parameter is specified, the private key specified is used instead of automatically generating it.
- `hashAlgorithm` (optional): The hash algorithm to be used when using `RSA` algorithm. Valid values are [`MD5`, `SHA1`, `SHA256`, `SHA512`]. This optional parameter is required when using `RSA` algorithm.
- `kskKeySize` (optional): The size of the Key Signing Key (KSK) in bits to be used when using `RSA` algorithm. This optional parameter is required when using `RSA` algorithm.
- `zskKeySize` (optional): The size of the Zone Signing Key (ZSK) in bits to be used when using `RSA` algorithm. This optional parameter is required when using `RSA` algorithm.
- `curve` (optional): The name of the curve to be used when using `ECDSA` or `EDDSA` algorithm. Valid values are [`P256`, `P384`] for `ECDSA` algorithm and [`ED25519`, `ED448`] for `EDDSA` algorithm. This optional parameter is required when using `ECDSA` or `EDDSA` algorithm.
- `curve` (optional): The name of the curve to be used when using `ECDSA` or `EDDSA` algorithm. Valid values are [`P256`, `P384`] for `ECDSA` algorithm and [`ED25519`, `ED448`] for `EDDSA` algorithm. This optional parameter is required when using `ECDSA` or `EDDSA` algorithm. The `MLDSA` algorithm does not use this parameter; it always generates an ML-DSA-44 key.
- `dnsKeyTtl` (optional): The TTL value to be used for DNSKEY records. Default value is `86400` when not specified.
- `zskRolloverDays` (optional): The frequency in days that the DNS server must automatically rollover the Zone Signing Keys (ZSK) in the zone. Valid range is 0-365 days where 0 disables rollover. Default value is `30` when not specified.
- `nxProof` (optional): The type of proof of non-existence that must be used for signing the zone. Valid values are [`NSEC`, `NSEC3`]. Default value is `NSEC` when not specified.
Expand Down Expand Up @@ -2975,11 +2975,11 @@ WHERE:
- `zone`: The name of the primary zone.
- `keyType`: The type of key for which the private key is to be generated. Valid values are [`KeySigningKey`, `ZoneSigningKey`].
- `rolloverDays` (optional): The frequency in days that the DNS server must automatically rollover the private key in the zone. Valid range is 0-365 days where 0 disables rollover. Default value is 90 days for Zone Signing Key (ZSK) and 0 days for Key Signing Key (KSK).
- `algorithm`: The algorithm to be used for signing. Valid values are [`RSA`, `ECDSA`, `EDDSA`].
- `algorithm`: The algorithm to be used for signing. Valid values are [`RSA`, `ECDSA`, `EDDSA`, `MLDSA`].
- `pemPrivateKey` (optional): Specifies a user generated private key in PEM format to add. When not specified a private key will be automatically generated.
- `hashAlgorithm` (optional): The hash algorithm to be used when using `RSA` algorithm. Valid values are [`MD5`, `SHA1`, `SHA256`, `SHA512`]. This optional parameter is required when using `RSA` algorithm.
- `keySize` (optional): The size of the generated private key in bits to be used when using `RSA` algorithm. This optional parameter is required when using `RSA` algorithm.
- `curve` (optional): The name of the curve to be used when using `ECDSA` or `EDDSA` algorithm. Valid values are [`P256`, `P384`] for `ECDSA` algorithm and [`ED25519`, `ED448`] for `EDDSA` algorithm. This optional parameter is required when using `ECDSA` or `EDDSA` algorithm.
- `curve` (optional): The name of the curve to be used when using `ECDSA` or `EDDSA` algorithm. Valid values are [`P256`, `P384`] for `ECDSA` algorithm and [`ED25519`, `ED448`] for `EDDSA` algorithm. This optional parameter is required when using `ECDSA` or `EDDSA` algorithm. The `MLDSA` algorithm does not use this parameter; it always generates an ML-DSA-44 key.

RESPONSE:
```
Expand Down Expand Up @@ -3193,7 +3193,7 @@ WHERE:
- `naptrReplacement` (optional): This parameter is required for adding the `NAPTR` record.
- `dname` (optional): The DNAME domain name. This option is required for adding `DNAME` record.
- `keyTag` (optional): This parameter is required for adding `DS` record.
- `algorithm` (optional): Valid values are [`RSAMD5`, `DSA`, `RSASHA1`, `DSA-NSEC3-SHA1`, `RSASHA1-NSEC3-SHA1`, `RSASHA256`, `RSASHA512`, `ECC-GOST`, `ECDSAP256SHA256`, `ECDSAP384SHA384`, `ED25519`, `ED448`]. This parameter is required for adding `DS` record.
- `algorithm` (optional): Valid values are [`RSAMD5`, `DSA`, `RSASHA1`, `DSA-NSEC3-SHA1`, `RSASHA1-NSEC3-SHA1`, `RSASHA256`, `RSASHA512`, `ECC-GOST`, `ECDSAP256SHA256`, `ECDSAP384SHA384`, `ED25519`, `ED448`, `MLDSA44`]. This parameter is required for adding `DS` record.
- `digestType` (optional): Valid values are [`SHA1`, `SHA256`, `GOST-R-34-11-94`, `SHA384`]. This parameter is required for adding `DS` record.
- `digest` (optional): A hex string value. This parameter is required for adding `DS` record.
- `sshfpAlgorithm` (optional): Valid values are [`RSA`, `DSA`, `ECDSA`, `Ed25519`, `Ed448`]. This parameter is required for adding `SSHFP` record.
Expand Down
96 changes: 96 additions & 0 deletions DnsServerCore/Dns/Dnssec/DnssecMldsaPrivateKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
Technitium DNS Server
Copyright (C) 2026 Shreyas Zare (shreyas@technitium.com)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

*/

using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using System.IO;
using TechnitiumLibrary.IO;
using TechnitiumLibrary.Net.Dns.Dnssec;
using TechnitiumLibrary.Net.Dns.ResourceRecords;

namespace DnsServerCore.Dns.Dnssec
{
class DnssecMldsaPrivateKey : DnssecPrivateKey
{
#region variables

MLDsaPrivateKeyParameters _privateKey;

#endregion

#region constructor

public DnssecMldsaPrivateKey(DnssecPrivateKeyType keyType, MLDsaPrivateKeyParameters privateKey)
: base(DnssecAlgorithm.MLDSA44, keyType)
{
_privateKey = privateKey;

InitDnsKey();
}

public DnssecMldsaPrivateKey(DnssecAlgorithm algorithm, BinaryReader bR, int version)
: base(algorithm, bR, version)
{
InitDnsKey();
}

#endregion

#region private

private void InitDnsKey()
{
InitDnsKey(new DnssecMldsaPublicKey(_privateKey.GetPublicKeyEncoded()));
}

#endregion

#region protected

protected override byte[] SignHash(byte[] hash)
{
//pure ML-DSA-44 signing with an empty context; must match the verifier's signer construction
MLDsaSigner signer = new MLDsaSigner(MLDsaParameters.ml_dsa_44, false);
signer.Init(true, _privateKey);
signer.BlockUpdate(hash, 0, hash.Length);

return signer.GenerateSignature();
}

protected override void ReadPrivateKeyFrom(BinaryReader bR)
{
switch (Algorithm)
{
case DnssecAlgorithm.MLDSA44:
_privateKey = MLDsaPrivateKeyParameters.FromSeed(MLDsaParameters.ml_dsa_44, bR.ReadBuffer());
break;

default:
throw new InvalidDataException();
}
}

protected override void WritePrivateKeyTo(BinaryWriter bW)
{
bW.WriteBuffer(_privateKey.GetSeed());
}

#endregion
}
}
18 changes: 18 additions & 0 deletions DnsServerCore/Dns/Dnssec/DnssecPrivateKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ public static DnssecPrivateKey Create(DnssecAlgorithm algorithm, DnssecPrivateKe
case DnssecAlgorithm.ED448:
return new DnssecEddsaPrivateKey(keyType, new Ed448PrivateKeyParameters(RandomNumberGenerator.GetBytes(57)));

case DnssecAlgorithm.MLDSA44:
return new DnssecMldsaPrivateKey(keyType, MLDsaPrivateKeyParameters.FromSeed(MLDsaParameters.ml_dsa_44, RandomNumberGenerator.GetBytes(32)));

default:
throw new NotSupportedException("DNSSEC algorithm is not supported: " + algorithm.ToString());
}
Expand Down Expand Up @@ -275,6 +278,18 @@ public static DnssecPrivateKey Create(DnssecAlgorithm algorithm, DnssecPrivateKe
return new DnssecEddsaPrivateKey(keyType, privateKey);
}

case DnssecAlgorithm.MLDSA44:
using (PemReader pemReader = new PemReader(new StringReader(pemPrivateKey)))
{
if ((pemReader.ReadObject() is not MLDsaPrivateKeyParameters privateKey) || (privateKey.Parameters != MLDsaParameters.ml_dsa_44))
throw new ArgumentException($"The ML-DSA ({(keyType == DnssecPrivateKeyType.KeySigningKey ? "KSK" : "ZSK")}) private key must be for ML-DSA-44.", nameof(pemPrivateKey));

if (privateKey.GetSeed() is null)
throw new ArgumentException($"The ML-DSA-44 ({(keyType == DnssecPrivateKeyType.KeySigningKey ? "KSK" : "ZSK")}) private key must use the seed encoding.", nameof(pemPrivateKey));

return new DnssecMldsaPrivateKey(keyType, privateKey);
}

default:
throw new NotSupportedException("DNSSEC algorithm is not supported: " + algorithm.ToString());
}
Expand Down Expand Up @@ -308,6 +323,9 @@ public static DnssecPrivateKey ReadFrom(BinaryReader bR)
case DnssecAlgorithm.ED448:
return new DnssecEddsaPrivateKey(algorithm, bR, version);

case DnssecAlgorithm.MLDSA44:
return new DnssecMldsaPrivateKey(algorithm, bR, version);

default:
throw new NotSupportedException("DNSSEC algorithm is not supported: " + algorithm.ToString());
}
Expand Down
1 change: 1 addition & 0 deletions DnsServerCore/Dns/Zones/PrimaryZone.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,7 @@ public void RolloverDnsKey(ushort keyTag)
case DnssecAlgorithm.ECDSAP384SHA384:
case DnssecAlgorithm.ED25519:
case DnssecAlgorithm.ED448:
case DnssecAlgorithm.MLDSA44:
GenerateAndAddPrivateKey(privateKey.KeyType, privateKey.Algorithm, privateKey.RolloverDays);
break;

Expand Down
34 changes: 34 additions & 0 deletions DnsServerCore/WebServiceZonesApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2285,6 +2285,22 @@ public void SignPrimaryZone(HttpContext context)
}
break;

case "MLDSA":
{
DnssecAlgorithm dnssecAlgorithm = DnssecAlgorithm.MLDSA44;

if (pemKskPrivateKey is null)
kskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.KeySigningKey);
else
kskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.KeySigningKey, pemKskPrivateKey);

if (pemZskPrivateKey is null)
zskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.ZoneSigningKey);
else
zskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.ZoneSigningKey, pemZskPrivateKey);
}
break;

default:
throw new NotSupportedException("Algorithm is not supported: " + algorithm);
}
Expand Down Expand Up @@ -2718,6 +2734,24 @@ public void AddPrimaryZoneDnssecPrivateKey(HttpContext context)
}
break;

case "MLDSA":
{
DnssecAlgorithm dnssecAlgorithm = DnssecAlgorithm.MLDSA44;

if (pemPrivateKey is null)
{
privateKey = _dnsWebService._dnsServer.AuthZoneManager.GenerateAndAddPrimaryZoneDnssecPrivateKey(zoneName, keyType, dnssecAlgorithm, rolloverDays);
}
else
{
privateKey = DnssecPrivateKey.Create(dnssecAlgorithm, keyType, pemPrivateKey);
privateKey.RolloverDays = rolloverDays;

_dnsWebService._dnsServer.AuthZoneManager.AddPrimaryZoneDnssecPrivateKey(zoneName, privateKey);
}
}
break;

default:
throw new NotSupportedException("Algorithm is not supported: " + algorithm);
}
Expand Down
8 changes: 8 additions & 0 deletions DnsServerCore/www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4539,6 +4539,7 @@ <h4 id="titleAddEditRecord" class="modal-title">Add Edit Record</h4>
<option value="ECDSAP384SHA384">ECDSAP384SHA384 (14)</option>
<option value="ED25519">ED25519 (15)</option>
<option value="ED448">ED448 (16)</option>
<option value="MLDSA44">MLDSA44 (18)</option>
</select>
</div>
</div>
Expand Down Expand Up @@ -5540,6 +5541,12 @@ <h4 class="modal-title">Sign Zone - <span id="lblDnssecSignZoneZoneName"></span>
EdDSA
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="rdDnssecSignZoneAlgorithm" id="rdDnssecSignZoneAlgorithmMldsa" value="MLDSA">
ML-DSA (post-quantum, experimental)
</label>
</div>
</div>
</div>

Expand Down Expand Up @@ -5887,6 +5894,7 @@ <h4 class="modal-title">DNSSEC Properties - <span id="lblDnssecPropertiesZoneNam
<option>RSA</option>
<option value="ECDSA">ECDSA (recommended)</option>
<option value="EDDSA">EdDSA</option>
<option value="MLDSA">ML-DSA (post-quantum, experimental)</option>
</select>
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions DnsServerCore/www/js/zone.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,15 @@ $(function () {
$("#divDnssecSignZoneEcdsaParameters").hide();
$("#divDnssecSignZoneEddsaParameters").show();

$("#divDnssecSignZoneRsaKskKeySize").hide();
$("#divDnssecSignZoneRsaZskKeySize").hide();
break;

case "MLDSA":
$("#divDnssecSignZoneRsaParameters").hide();
$("#divDnssecSignZoneEcdsaParameters").hide();
$("#divDnssecSignZoneEddsaParameters").hide();

$("#divDnssecSignZoneRsaKskKeySize").hide();
$("#divDnssecSignZoneRsaZskKeySize").hide();
break;
Expand Down Expand Up @@ -493,6 +502,14 @@ $(function () {
$("#divDnssecPropertiesAddKeyEcdsaParameters").hide();
$("#divDnssecPropertiesAddKeyEddsaParameters").show();

$("#divDnssecPropertiesAddKeyRsaKeySize").hide();
break;

case "MLDSA":
$("#divDnssecPropertiesAddKeyRsaParameters").hide();
$("#divDnssecPropertiesAddKeyEcdsaParameters").hide();
$("#divDnssecPropertiesAddKeyEddsaParameters").hide();

$("#divDnssecPropertiesAddKeyRsaKeySize").hide();
break;
}
Expand Down