|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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") |
|
|
|
|
|
nonce = self._compute_nonce(msg_hash) |
|
|
|
|
|
z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) |
|
sig_pair = self._key._sign(z, nonce) |
|
|
|
|
|
if self._encoding == 'binary': |
|
output = b"".join([long_to_bytes(x, self._order_bytes) |
|
for x in sig_pair]) |
|
else: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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") |
|
|
|
return False |
|
|
|
|
|
class DeterministicDsaSigScheme(DssSigScheme): |
|
|
|
|
|
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: |
|
|
|
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""" |
|
|
|
|
|
|
|
h1 = mhash.digest() |
|
|
|
mask_v = b'\x01' * mhash.digest_size |
|
|
|
nonce_k = b'\x00' * mhash.digest_size |
|
|
|
for int_oct in (b'\x00', b'\x01'): |
|
|
|
nonce_k = HMAC.new(nonce_k, |
|
mask_v + int_oct + |
|
self._int2octets(self._private_key) + |
|
self._bits2octets(h1), mhash).digest() |
|
|
|
mask_v = HMAC.new(nonce_k, mask_v, mhash).digest() |
|
|
|
nonce = -1 |
|
while not (0 < nonce < self._order): |
|
|
|
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() |
|
|
|
|
|
mask_t = b"" |
|
|
|
|
|
while len(mask_t) < self._order_bytes: |
|
mask_v = HMAC.new(nonce_k, mask_v, mhash).digest() |
|
mask_t += mask_v |
|
|
|
|
|
nonce = self._bits2int(mask_t) |
|
return nonce |
|
|
|
def _valid_hash(self, msg_hash): |
|
return True |
|
|
|
|
|
class FipsDsaSigScheme(DssSigScheme): |
|
|
|
|
|
|
|
|
|
_fips_186_3_L_N = ( |
|
(1024, 160), |
|
(2048, 224), |
|
(2048, 256), |
|
(3072, 256) |
|
) |
|
|
|
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): |
|
|
|
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() |
|
|
|
|
|
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 |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|