Viewing file: DSS.py (14.94 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# # Signature/DSS.py : DSS.py # # =================================================================== # # Copyright (c) 2014, Legrandin <helderijs@gmail.com> # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ===================================================================
from Crypto.Util.asn1 import DerSequence from Crypto.Util.number import long_to_bytes from Crypto.Math.Numbers import Integer
from Crypto.Hash import HMAC from Crypto.PublicKey.ECC import EccKey from Crypto.PublicKey.DSA import DsaKey
__all__ = ['DssSigScheme', 'new']
class DssSigScheme(object): """A (EC)DSA signature object. Do not instantiate directly. Use :func:`Crypto.Signature.DSS.new`. """
def __init__(self, key, encoding, order): """Create a new Digital Signature Standard (DSS) object.
Do not instantiate this object directly, use `Crypto.Signature.DSS.new` instead. """
self._key = key self._encoding = encoding self._order = order
self._order_bits = self._order.size_in_bits() self._order_bytes = (self._order_bits - 1) // 8 + 1
def can_sign(self): """Return ``True`` if this signature object can be used for signing messages."""
return self._key.has_private()
def _compute_nonce(self, msg_hash): raise NotImplementedError("To be provided by subclasses")
def _valid_hash(self, msg_hash): raise NotImplementedError("To be provided by subclasses")
def sign(self, msg_hash): """Compute the DSA/ECDSA signature of a message.
Args: msg_hash (hash object): The hash that was carried out over the message. The object belongs to the :mod:`Crypto.Hash` package. Under mode ``'fips-186-3'``, the hash must be a FIPS approved secure hash (SHA-2 or SHA-3).
:return: The signature as ``bytes`` :raise ValueError: if the hash algorithm is incompatible to the (EC)DSA key :raise TypeError: if the (EC)DSA key has no private half """
if not self._key.has_private(): raise TypeError("Private key is needed to sign")
if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong")
# Generate the nonce k (critical!) nonce = self._compute_nonce(msg_hash)
# Perform signature using the raw API z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) sig_pair = self._key._sign(z, nonce)
# Encode the signature into a single byte string if self._encoding == 'binary': output = b"".join([long_to_bytes(x, self._order_bytes) for x in sig_pair]) else: # Dss-sig ::= SEQUENCE { # r INTEGER, # s INTEGER # } # Ecdsa-Sig-Value ::= SEQUENCE { # r INTEGER, # s INTEGER # } output = DerSequence(sig_pair).encode()
return output
def verify(self, msg_hash, signature): """Check if a certain (EC)DSA signature is authentic.
Args: msg_hash (hash object): The hash that was carried out over the message. This is an object belonging to the :mod:`Crypto.Hash` module. Under mode ``'fips-186-3'``, the hash must be a FIPS approved secure hash (SHA-2 or SHA-3).
signature (``bytes``): The signature that needs to be validated.
:raise ValueError: if the signature is not authentic """
if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong")
if self._encoding == 'binary': if len(signature) != (2 * self._order_bytes): raise ValueError("The signature is not authentic (length)") r_prime, s_prime = [Integer.from_bytes(x) for x in (signature[:self._order_bytes], signature[self._order_bytes:])] else: try: der_seq = DerSequence().decode(signature, strict=True) except (ValueError, IndexError): raise ValueError("The signature is not authentic (DER)") if len(der_seq) != 2 or not der_seq.hasOnlyInts(): raise ValueError("The signature is not authentic (DER content)") r_prime, s_prime = Integer(der_seq[0]), Integer(der_seq[1])
if not (0 < r_prime < self._order) or not (0 < s_prime < self._order): raise ValueError("The signature is not authentic (d)")
z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) result = self._key._verify(z, (r_prime, s_prime)) if not result: raise ValueError("The signature is not authentic") # Make PyCrypto code to fail return False
class DeterministicDsaSigScheme(DssSigScheme): # Also applicable to ECDSA
def __init__(self, key, encoding, order, private_key): super(DeterministicDsaSigScheme, self).__init__(key, encoding, order) self._private_key = private_key
def _bits2int(self, bstr): """See 2.3.2 in RFC6979"""
result = Integer.from_bytes(bstr) q_len = self._order.size_in_bits() b_len = len(bstr) * 8 if b_len > q_len: # Only keep leftmost q_len bits result >>= (b_len - q_len) return result
def _int2octets(self, int_mod_q): """See 2.3.3 in RFC6979"""
assert 0 < int_mod_q < self._order return long_to_bytes(int_mod_q, self._order_bytes)
def _bits2octets(self, bstr): """See 2.3.4 in RFC6979"""
z1 = self._bits2int(bstr) if z1 < self._order: z2 = z1 else: z2 = z1 - self._order return self._int2octets(z2)
def _compute_nonce(self, mhash): """Generate k in a deterministic way"""
# See section 3.2 in RFC6979.txt # Step a h1 = mhash.digest() # Step b mask_v = b'\x01' * mhash.digest_size # Step c nonce_k = b'\x00' * mhash.digest_size
for int_oct in (b'\x00', b'\x01'): # Step d/f nonce_k = HMAC.new(nonce_k, mask_v + int_oct + self._int2octets(self._private_key) + self._bits2octets(h1), mhash).digest() # Step e/g mask_v = HMAC.new(nonce_k, mask_v, mhash).digest()
nonce = -1 while not (0 < nonce < self._order): # Step h.C (second part) if nonce != -1: nonce_k = HMAC.new(nonce_k, mask_v + b'\x00', mhash).digest() mask_v = HMAC.new(nonce_k, mask_v, mhash).digest()
# Step h.A mask_t = b""
# Step h.B while len(mask_t) < self._order_bytes: mask_v = HMAC.new(nonce_k, mask_v, mhash).digest() mask_t += mask_v
# Step h.C (first part) nonce = self._bits2int(mask_t) return nonce
def _valid_hash(self, msg_hash): return True
class FipsDsaSigScheme(DssSigScheme):
#: List of L (bit length of p) and N (bit length of q) combinations #: that are allowed by FIPS 186-3. The security level is provided in #: Table 2 of FIPS 800-57 (rev3). _fips_186_3_L_N = ( (1024, 160), # 80 bits (SHA-1 or stronger) (2048, 224), # 112 bits (SHA-224 or stronger) (2048, 256), # 128 bits (SHA-256 or stronger) (3072, 256) # 256 bits (SHA-512) )
def __init__(self, key, encoding, order, randfunc): super(FipsDsaSigScheme, self).__init__(key, encoding, order) self._randfunc = randfunc
L = Integer(key.p).size_in_bits() if (L, self._order_bits) not in self._fips_186_3_L_N: error = ("L/N (%d, %d) is not compliant to FIPS 186-3" % (L, self._order_bits)) raise ValueError(error)
def _compute_nonce(self, msg_hash): # hash is not used return Integer.random_range(min_inclusive=1, max_exclusive=self._order, randfunc=self._randfunc)
def _valid_hash(self, msg_hash): """Verify that SHA-1, SHA-2 or SHA-3 are used""" return (msg_hash.oid == "1.3.14.3.2.26" or msg_hash.oid.startswith("2.16.840.1.101.3.4.2."))
class FipsEcDsaSigScheme(DssSigScheme):
def __init__(self, key, encoding, order, randfunc): super(FipsEcDsaSigScheme, self).__init__(key, encoding, order) self._randfunc = randfunc
def _compute_nonce(self, msg_hash): return Integer.random_range(min_inclusive=1, max_exclusive=self._key._curve.order, randfunc=self._randfunc)
def _valid_hash(self, msg_hash): """Verify that the strength of the hash matches or exceeds the strength of the EC. We fail if the hash is too weak."""
modulus_bits = self._key.pointQ.size_in_bits()
# SHS: SHA-2, SHA-3, truncated SHA-512 sha224 = ("2.16.840.1.101.3.4.2.4", "2.16.840.1.101.3.4.2.7", "2.16.840.1.101.3.4.2.5") sha256 = ("2.16.840.1.101.3.4.2.1", "2.16.840.1.101.3.4.2.8", "2.16.840.1.101.3.4.2.6") sha384 = ("2.16.840.1.101.3.4.2.2", "2.16.840.1.101.3.4.2.9") sha512 = ("2.16.840.1.101.3.4.2.3", "2.16.840.1.101.3.4.2.10") shs = sha224 + sha256 + sha384 + sha512
try: result = msg_hash.oid in shs except AttributeError: result = False return result
def new(key, mode, encoding='binary', randfunc=None): """Create a signature object :class:`DssSigScheme` that can perform (EC)DSA signature or verification.
.. note:: Refer to `NIST SP 800 Part 1 Rev 4`_ (or newer release) for an overview of the recommended key lengths.
Args: key (:class:`Crypto.PublicKey.DSA` or :class:`Crypto.PublicKey.ECC`): The key to use for computing the signature (*private* keys only) or for verifying one. For DSA keys, let ``L`` and ``N`` be the bit lengths of the modulus ``p`` and of ``q``: the pair ``(L,N)`` must appear in the following list, in compliance to section 4.2 of `FIPS 186-4`_:
- (1024, 160) *legacy only; do not create new signatures with this* - (2048, 224) *deprecated; do not create new signatures with this* - (2048, 256) - (3072, 256)
For ECC, only keys over P-224, P-256, P-384, and P-521 are accepted.
mode (string): The parameter can take these values:
- ``'fips-186-3'``. The signature generation is randomized and carried out according to `FIPS 186-3`_: the nonce ``k`` is taken from the RNG. - ``'deterministic-rfc6979'``. The signature generation is not randomized. See RFC6979_.
encoding (string): How the signature is encoded. This value determines the output of :meth:`sign` and the input to :meth:`verify`.
The following values are accepted:
- ``'binary'`` (default), the signature is the raw concatenation of ``r`` and ``s``. It is defined in the IEEE P.1363 standard. For DSA, the size in bytes of the signature is ``N/4`` bytes (e.g. 64 for ``N=256``). For ECDSA, the signature is always twice the length of a point coordinate (e.g. 64 bytes for P-256).
- ``'der'``, the signature is a ASN.1 DER SEQUENCE with two INTEGERs (``r`` and ``s``). It is defined in RFC3279_. The size of the signature is variable.
randfunc (callable): A function that returns random ``bytes``, of a given length. If omitted, the internal RNG is used. Only applicable for the *'fips-186-3'* mode.
.. _FIPS 186-3: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf .. _FIPS 186-4: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf .. _NIST SP 800 Part 1 Rev 4: http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r4.pdf .. _RFC6979: http://tools.ietf.org/html/rfc6979 .. _RFC3279: https://tools.ietf.org/html/rfc3279#section-2.2.2 """
# The goal of the 'mode' parameter is to avoid to # have the current version of the standard as default. # # Over time, such version will be superseded by (for instance) # FIPS 186-4 and it will be odd to have -3 as default.
if encoding not in ('binary', 'der'): raise ValueError("Unknown encoding '%s'" % encoding)
if isinstance(key, EccKey): order = key._curve.order private_key_attr = 'd' if key._curve.name == "ed25519": raise ValueError("ECC key is not on a NIST P curve") elif isinstance(key, DsaKey): order = Integer(key.q) private_key_attr = 'x' else: raise ValueError("Unsupported key type " + str(type(key)))
if key.has_private(): private_key = getattr(key, private_key_attr) else: private_key = None
if mode == 'deterministic-rfc6979': return DeterministicDsaSigScheme(key, encoding, order, private_key) elif mode == 'fips-186-3': if isinstance(key, EccKey): return FipsEcDsaSigScheme(key, encoding, order, randfunc) else: return FipsDsaSigScheme(key, encoding, order, randfunc) else: raise ValueError("Unknown DSS mode '%s'" % mode)
|