from ecpy.curves import Curve from ecpy.keys import ECPrivateKey from ecpy.eddsa import EDDSA import secrets, hashlib, binascii curve = Curve.get_curve('Ed448') signer = EDDSA(hashlib.shake_256, hash_len=114) privKey = ECPrivateKey(secrets.randbits(57 * 8), curve) pubKey = signer.get_public_key(privKey, hashlib.shake_256, hash_len=114) print("Private key (57 bytes):", privKey) print("Public key (compressed, 57 bytes): ", binascii.hexlify(curve.encode_point(pubKey.W))) print("Public key (point): ", pubKey) msg = b'Message for Ed448 signing' signature = signer.sign(msg, privKey) print("Signature (114 bytes):", binascii.hexlify(signature)) valid = signer.verify(msg, signature, pubKey) print("Valid signature?", valid) valid = signer.verify(b'Tampered msg', signature, pubKey) print("Valid signature?", valid)
def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): """Validate an RRset against a single signature rdata :param rrset: The RRset to validate :type rrset: :py:data:`dns.rrset.RRset` or (:py:data:`dns.name.Name`, :py:data:`dns.rdataset.Rdataset`) :param rrsig: Signature to validate :type rrsig: :py:data:`dns.rdata.Rdata` :param keys: Key dictionary, used to find the DNSKEY associated with a given name. The dictionary is keyed by a :py:data:`dns.name.Name`, and has :py:data:`dns.node.Node` or :py:data:`dns.rdataset.Rdataset` values. :type keys: dictionary :param origin: Origin to use for relative name, defaults to None :type origin: :py:data:`dns.name.Name`, optional :param now: time to use when validating the signatures, in seconds since the UNIX epoch, defaults to current time :type now: integer, optional :raises ValidationFailure: RRSig expired :raises ValidationFailure: RRSig not yet valid :raises ValidationFailure: Invalid public key :raises ValidationFailure: Invalid ECDSA key :raises ValidationFailure: Unknown algorithm :raises ValueError: Generic Value Error :raises ValidationFailure: Verify failure :raises UnsupportedAlgorithm: Algorithm isn't supported by dnspython :return: none :rtype: none .. todo:: Fill in missing infos """ if isinstance(origin, str): origin = dns.name.from_text(origin, dns.name.root) candidate_keys = _find_candidate_keys(keys, rrsig) if candidate_keys is None: raise ValidationFailure('unknown key') for candidate_key in candidate_keys: # For convenience, allow the rrset to be specified as a (name, # rdataset) tuple as well as a proper rrset if isinstance(rrset, tuple): rrname = rrset[0] rdataset = rrset[1] else: rrname = rrset.name rdataset = rrset if now is None: now = time.time() if rrsig.expiration < now: raise ValidationFailure('expired') if rrsig.inception > now: raise ValidationFailure('not yet valid') if _is_rsa(rrsig.algorithm): keyptr = candidate_key.key (bytes_, ) = struct.unpack('!B', keyptr[0:1]) keyptr = keyptr[1:] if bytes_ == 0: (bytes_, ) = struct.unpack('!H', keyptr[0:2]) keyptr = keyptr[2:] rsa_e = keyptr[0:bytes_] rsa_n = keyptr[bytes_:] try: pubkey = CryptoRSA.construct( (number.bytes_to_long(rsa_n), number.bytes_to_long(rsa_e))) except ValueError: raise ValidationFailure('invalid public key') sig = rrsig.signature elif _is_dsa(rrsig.algorithm): keyptr = candidate_key.key (t, ) = struct.unpack('!B', keyptr[0:1]) keyptr = keyptr[1:] octets = 64 + t * 8 dsa_q = keyptr[0:20] keyptr = keyptr[20:] dsa_p = keyptr[0:octets] keyptr = keyptr[octets:] dsa_g = keyptr[0:octets] keyptr = keyptr[octets:] dsa_y = keyptr[0:octets] pubkey = CryptoDSA.construct( (number.bytes_to_long(dsa_y), number.bytes_to_long(dsa_g), number.bytes_to_long(dsa_p), number.bytes_to_long(dsa_q))) sig = rrsig.signature[1:] elif _is_ecdsa(rrsig.algorithm): keyptr = candidate_key.key if rrsig.algorithm == ECDSAP256SHA256: curve = 'secp256r1' octets = 32 else: curve = 'secp384r1' octets = 48 ecdsa_x = keyptr[0:octets] ecdsa_y = keyptr[octets:octets * 2] pubkey = CryptoECC.construct(curve=curve, point_x=number.bytes_to_long(ecdsa_x), point_y=number.bytes_to_long(ecdsa_y)) sig = rrsig.signature elif _is_eddsa(rrsig.algorithm): keyptr = candidate_key.key if not (_have_ecpy and sys.version_info >= (3, 6)): #pylint: disable=line-too-long raise ImportError( 'DNSSEC validation for algorithm %u requires ecpy library and Python 3.6 or newer' % rrsig.algorithm) if rrsig.algorithm == ED25519: curve = 'Ed25519' else: curve = 'Ed448' point = Curve.get_curve(curve).decode_point(keyptr) pubkey = ECPublicKey(point) sig = rrsig.signature elif _is_gost(rrsig.algorithm): raise UnsupportedAlgorithm( 'algorithm "%s" not supported by dnspython' % algorithm_to_text(rrsig.algorithm)) else: raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) hash = _make_hash(rrsig.algorithm) hash.update(_to_rdata(rrsig, origin)[:18]) hash.update(rrsig.signer.to_digestable(origin)) if rrsig.labels < len(rrname) - 1: suffix = rrname.split(rrsig.labels + 1)[1] rrname = dns.name.from_text('*', suffix) rrnamebuf = rrname.to_digestable(origin) rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass, rrsig.original_ttl) rrlist = sorted(rdataset) for rr in rrlist: hash.update(rrnamebuf) hash.update(rrfixed) rrdata = rr.to_digestable(origin) rrlen = struct.pack('!H', len(rrdata)) hash.update(rrlen) hash.update(rrdata) try: if _is_rsa(rrsig.algorithm): verifier = pkcs1_15.new(pubkey) # will raise ValueError if verify fails: verifier.verify(hash, sig) elif _is_dsa(rrsig.algorithm) or _is_ecdsa(rrsig.algorithm): verifier = DSS.new(pubkey, 'fips-186-3') verifier.verify(hash, sig) elif _is_eddsa(rrsig.algorithm): if rrsig.algorithm == ED25519: verifier = EDDSA(hashlib.sha512) else: verifier = EDDSA(hashlib.shake_256, 114) if not verifier.verify(hash.value, sig, pubkey): raise ValueError else: # Raise here for code clarity; this won't actually ever happen # since if the algorithm is really unknown we'd already have # raised an exception above raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) # If we got here, we successfully verified so we can return without error return except ValueError: # this happens on an individual validation failure continue # nothing verified -- raise failure: raise ValidationFailure('verify failure')