Add support for deterministic signatures and make them by default for ECDSA.
verify: bool = False,
policy: Optional[Policy] = None,
origin: Optional[dns.name.Name] = None,
+ deterministic: bool = True,
) -> RRSIG:
"""Sign RRset using private key.
names in the rrset (including its owner name) must be absolute; otherwise the
specified origin will be used to make names absolute when signing.
+ *deterministic*, a ``bool``. If ``True``, the default, use deterministic
+ (reproducible) signatures when supported by the algorithm used for signing.
+ Currently, this only affects ECDSA.
+
Raises ``DeniedByPolicy`` if the signature is denied by policy.
"""
except UnsupportedAlgorithm:
raise TypeError("Unsupported key algorithm")
- signature = signing_key.sign(data, verify)
+ signature = signing_key.sign(data, verify, deterministic)
return cast(RRSIG, rrsig_template.replace(signature=signature))
lifetime: Optional[int] = None,
policy: Optional[Policy] = None,
origin: Optional[dns.name.Name] = None,
+ deterministic: bool = True,
) -> None:
"""Default RRset signer"""
signer=signer,
policy=policy,
origin=origin,
+ deterministic=deterministic,
)
txn.add(rrset.name, rrset.ttl, rrsig)
nsec3: Optional[NSEC3PARAM] = None,
rrset_signer: Optional[RRsetSigner] = None,
policy: Optional[Policy] = None,
+ deterministic: bool = True,
) -> None:
"""Sign zone.
function requires two arguments: transaction and RRset. If the not specified,
``dns.dnssec.default_rrset_signer`` will be used.
+ *deterministic*, a ``bool``. If ``True``, the default, use deterministic
+ (reproducible) signatures when supported by the algorithm used for signing.
+ Currently, this only affects ECDSA.
+
Returns ``None``.
"""
lifetime=lifetime,
policy=policy,
origin=zone.origin,
+ deterministic=deterministic,
)
return _sign_zone_nsec(zone, _txn, _rrset_signer)
pass
@abstractmethod
- def sign(self, data: bytes, verify: bool = False) -> bytes:
+ def sign(
+ self,
+ data: bytes,
+ verify: bool = False,
+ deterministic: bool = True,
+ ) -> bytes:
"""Sign DNSSEC data"""
@abstractmethod
import struct
+from typing import Optional
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
key_cls = dsa.DSAPrivateKey
public_cls = PublicDSA
- def sign(self, data: bytes, verify: bool = False) -> bytes:
+ def sign(
+ self,
+ data: bytes,
+ verify: bool = False,
+ deterministic: bool = True,
+ ) -> bytes:
"""Sign using a private key per RFC 2536, section 3."""
public_dsa_key = self.key.public_key()
if public_dsa_key.key_size > 1024:
+from typing import Optional
+
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, utils
key_cls = ec.EllipticCurvePrivateKey
public_cls = PublicECDSA
- def sign(self, data: bytes, verify: bool = False) -> bytes:
+ def sign(
+ self,
+ data: bytes,
+ verify: bool = False,
+ deterministic: bool = True,
+ ) -> bytes:
"""Sign using a private key per RFC 6605, section 4."""
- der_signature = self.key.sign(data, ec.ECDSA(self.public_cls.chosen_hash))
+ algorithm = ec.ECDSA(
+ self.public_cls.chosen_hash, deterministic_signing=deterministic
+ )
+ der_signature = self.key.sign(data, algorithm)
dsa_r, dsa_s = utils.decode_dss_signature(der_signature)
signature = int.to_bytes(
dsa_r, length=self.public_cls.octets, byteorder="big"
-from typing import Type
+from typing import Optional, Type
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed448, ed25519
class PrivateEDDSA(CryptographyPrivateKey):
public_cls: Type[PublicEDDSA]
- def sign(self, data: bytes, verify: bool = False) -> bytes:
+ def sign(
+ self,
+ data: bytes,
+ verify: bool = False,
+ deterministic: bool = True,
+ ) -> bytes:
"""Sign using a private key per RFC 8080, section 4."""
signature = self.key.sign(data)
if verify:
import math
import struct
+from typing import Optional
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
public_cls = PublicRSA
default_public_exponent = 65537
- def sign(self, data: bytes, verify: bool = False) -> bytes:
+ def sign(
+ self,
+ data: bytes,
+ verify: bool = False,
+ deterministic: bool = True,
+ ) -> bytes:
"""Sign using a private key per RFC 3110, section 3."""
signature = self.key.sign(data, padding.PKCS1v15(), self.public_cls.chosen_hash)
if verify:
key = ec.generate_private_key(curve=ec.SECP256R1(), backend=default_backend())
self._test_signature(key, dns.dnssec.Algorithm.ECDSAP256SHA256, abs_soa)
+ def testDeterministicSignatureECDSAP256SHA256(self): # type: () -> None
+ key = ec.generate_private_key(curve=ec.SECP256R1(), backend=default_backend())
+ inception = time.time()
+ rrsigset1 = self._test_signature(
+ key,
+ dns.dnssec.Algorithm.ECDSAP256SHA256,
+ abs_soa,
+ inception=inception,
+ deterministic=True,
+ )
+ rrsigset2 = self._test_signature(
+ key,
+ dns.dnssec.Algorithm.ECDSAP256SHA256,
+ abs_soa,
+ inception=inception,
+ deterministic=True,
+ )
+ assert rrsigset1 == rrsigset2
+
+ def testNonDeterministicSignatureECDSAP256SHA256(self): # type: () -> None
+ key = ec.generate_private_key(curve=ec.SECP256R1(), backend=default_backend())
+ inception = time.time()
+ rrsigset1 = self._test_signature(
+ key,
+ dns.dnssec.Algorithm.ECDSAP256SHA256,
+ abs_soa,
+ inception=inception,
+ deterministic=False,
+ )
+ rrsigset2 = self._test_signature(
+ key,
+ dns.dnssec.Algorithm.ECDSAP256SHA256,
+ abs_soa,
+ inception=inception,
+ deterministic=False,
+ )
+ assert rrsigset1 != rrsigset2
+
def testSignatureECDSAP384SHA384(self): # type: () -> None
key = ec.generate_private_key(curve=ec.SECP384R1(), backend=default_backend())
self._test_signature(key, dns.dnssec.Algorithm.ECDSAP384SHA384, abs_soa)
rrsigset = self._test_signature(key, dns.dnssec.Algorithm.ED448, rrset)
self.assertEqual(rrsigset[0].labels, 2)
- def _test_signature(self, key, algorithm, rrset, signer=None, policy=None):
+ def _test_signature(
+ self,
+ key,
+ algorithm,
+ rrset,
+ signer=None,
+ policy=None,
+ inception=None,
+ deterministic=True,
+ ):
ttl = 60
lifetime = 3600
if isinstance(rrset, tuple):
rrset=rrset,
private_key=key,
dnskey=dnskey,
+ inception=inception,
lifetime=lifetime,
signer=signer,
verify=True,
+ deterministic=deterministic,
policy=policy,
)
keys = {signer: dnskey_rrset}