| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- # ===================================================================
- #
- # Copyright (c) 2022, 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.Math.Numbers import Integer
- from Crypto.Hash import SHA512, SHAKE256
- from Crypto.Util.py3compat import bchr, is_bytes
- from Crypto.PublicKey.ECC import (EccKey,
- construct,
- _import_ed25519_public_key,
- _import_ed448_public_key)
- def import_public_key(encoded):
- """Create a new Ed25519 or Ed448 public key object,
- starting from the key encoded as raw ``bytes``,
- in the format described in RFC8032.
- Args:
- encoded (bytes):
- The EdDSA public key to import.
- It must be 32 bytes for Ed25519, and 57 bytes for Ed448.
- Returns:
- :class:`Crypto.PublicKey.EccKey` : a new ECC key object.
- Raises:
- ValueError: when the given key cannot be parsed.
- """
- if len(encoded) == 32:
- x, y = _import_ed25519_public_key(encoded)
- curve_name = "Ed25519"
- elif len(encoded) == 57:
- x, y = _import_ed448_public_key(encoded)
- curve_name = "Ed448"
- else:
- raise ValueError("Not an EdDSA key (%d bytes)" % len(encoded))
- return construct(curve=curve_name, point_x=x, point_y=y)
- def import_private_key(encoded):
- """Create a new Ed25519 or Ed448 private key object,
- starting from the key encoded as raw ``bytes``,
- in the format described in RFC8032.
- Args:
- encoded (bytes):
- The EdDSA private key to import.
- It must be 32 bytes for Ed25519, and 57 bytes for Ed448.
- Returns:
- :class:`Crypto.PublicKey.EccKey` : a new ECC key object.
- Raises:
- ValueError: when the given key cannot be parsed.
- """
- if len(encoded) == 32:
- curve_name = "ed25519"
- elif len(encoded) == 57:
- curve_name = "ed448"
- else:
- raise ValueError("Incorrect length. Only EdDSA private keys are supported.")
- # Note that the private key is truly a sequence of random bytes,
- # so we cannot check its correctness in any way.
- return construct(seed=encoded, curve=curve_name)
- class EdDSASigScheme(object):
- """An EdDSA signature object.
- Do not instantiate directly.
- Use :func:`Crypto.Signature.eddsa.new`.
- """
- def __init__(self, key, context):
- """Create a new EdDSA object.
- Do not instantiate this object directly,
- use `Crypto.Signature.DSS.new` instead.
- """
- self._key = key
- self._context = context
- self._A = key._export_eddsa()
- self._order = key._curve.order
- def can_sign(self):
- """Return ``True`` if this signature object can be used
- for signing messages."""
- return self._key.has_private()
- def sign(self, msg_or_hash):
- """Compute the EdDSA signature of a message.
- Args:
- msg_or_hash (bytes or a hash object):
- The message to sign (``bytes``, in case of *PureEdDSA*) or
- the hash that was carried out over the message (hash object, for *HashEdDSA*).
- The hash object must be :class:`Crypto.Hash.SHA512` for Ed25519,
- and :class:`Crypto.Hash.SHAKE256` object for Ed448.
- :return: The signature as ``bytes``. It is always 64 bytes for Ed25519, and 114 bytes for Ed448.
- :raise TypeError: if the EdDSA key has no private half
- """
- if not self._key.has_private():
- raise TypeError("Private key is needed to sign")
- if self._key._curve.name == "ed25519":
- ph = isinstance(msg_or_hash, SHA512.SHA512Hash)
- if not (ph or is_bytes(msg_or_hash)):
- raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash")
- eddsa_sign_method = self._sign_ed25519
- elif self._key._curve.name == "ed448":
- ph = isinstance(msg_or_hash, SHAKE256.SHAKE256_XOF)
- if not (ph or is_bytes(msg_or_hash)):
- raise TypeError("'msg_or_hash' must be bytes of a SHAKE256 hash")
- eddsa_sign_method = self._sign_ed448
- else:
- raise ValueError("Incorrect curve for EdDSA")
- return eddsa_sign_method(msg_or_hash, ph)
- def _sign_ed25519(self, msg_or_hash, ph):
- if self._context or ph:
- flag = int(ph)
- # dom2(flag, self._context)
- dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \
- bchr(len(self._context)) + self._context
- else:
- dom2 = b''
- PHM = msg_or_hash.digest() if ph else msg_or_hash
- # See RFC 8032, section 5.1.6
- # Step 2
- r_hash = SHA512.new(dom2 + self._key._prefix + PHM).digest()
- r = Integer.from_bytes(r_hash, 'little') % self._order
- # Step 3
- R_pk = EccKey(point=r * self._key._curve.G)._export_eddsa()
- # Step 4
- k_hash = SHA512.new(dom2 + R_pk + self._A + PHM).digest()
- k = Integer.from_bytes(k_hash, 'little') % self._order
- # Step 5
- s = (r + k * self._key.d) % self._order
- return R_pk + s.to_bytes(32, 'little')
- def _sign_ed448(self, msg_or_hash, ph):
- flag = int(ph)
- # dom4(flag, self._context)
- dom4 = b'SigEd448' + bchr(flag) + \
- bchr(len(self._context)) + self._context
- PHM = msg_or_hash.read(64) if ph else msg_or_hash
- # See RFC 8032, section 5.2.6
- # Step 2
- r_hash = SHAKE256.new(dom4 + self._key._prefix + PHM).read(114)
- r = Integer.from_bytes(r_hash, 'little') % self._order
- # Step 3
- R_pk = EccKey(point=r * self._key._curve.G)._export_eddsa()
- # Step 4
- k_hash = SHAKE256.new(dom4 + R_pk + self._A + PHM).read(114)
- k = Integer.from_bytes(k_hash, 'little') % self._order
- # Step 5
- s = (r + k * self._key.d) % self._order
- return R_pk + s.to_bytes(57, 'little')
- def verify(self, msg_or_hash, signature):
- """Check if an EdDSA signature is authentic.
- Args:
- msg_or_hash (bytes or a hash object):
- The message to verify (``bytes``, in case of *PureEdDSA*) or
- the hash that was carried out over the message (hash object, for *HashEdDSA*).
- The hash object must be :class:`Crypto.Hash.SHA512` object for Ed25519,
- and :class:`Crypto.Hash.SHAKE256` for Ed448.
- signature (``bytes``):
- The signature that needs to be validated.
- It must be 64 bytes for Ed25519, and 114 bytes for Ed448.
- :raise ValueError: if the signature is not authentic
- """
- if self._key._curve.name == "ed25519":
- ph = isinstance(msg_or_hash, SHA512.SHA512Hash)
- if not (ph or is_bytes(msg_or_hash)):
- raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash")
- eddsa_verify_method = self._verify_ed25519
- elif self._key._curve.name == "ed448":
- ph = isinstance(msg_or_hash, SHAKE256.SHAKE256_XOF)
- if not (ph or is_bytes(msg_or_hash)):
- raise TypeError("'msg_or_hash' must be bytes of a SHAKE256 hash")
- eddsa_verify_method = self._verify_ed448
- else:
- raise ValueError("Incorrect curve for EdDSA")
- return eddsa_verify_method(msg_or_hash, signature, ph)
- def _verify_ed25519(self, msg_or_hash, signature, ph):
- if len(signature) != 64:
- raise ValueError("The signature is not authentic (length)")
- if self._context or ph:
- flag = int(ph)
- dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \
- bchr(len(self._context)) + self._context
- else:
- dom2 = b''
- PHM = msg_or_hash.digest() if ph else msg_or_hash
- # Section 5.1.7
- # Step 1
- try:
- R = import_public_key(signature[:32]).pointQ
- except ValueError:
- raise ValueError("The signature is not authentic (R)")
- s = Integer.from_bytes(signature[32:], 'little')
- if s > self._order:
- raise ValueError("The signature is not authentic (S)")
- # Step 2
- k_hash = SHA512.new(dom2 + signature[:32] + self._A + PHM).digest()
- k = Integer.from_bytes(k_hash, 'little') % self._order
- # Step 3
- point1 = s * 8 * self._key._curve.G
- # OPTIMIZE: with double-scalar multiplication, with no SCA
- # countermeasures because it is public values
- point2 = 8 * R + k * 8 * self._key.pointQ
- if point1 != point2:
- raise ValueError("The signature is not authentic")
- def _verify_ed448(self, msg_or_hash, signature, ph):
- if len(signature) != 114:
- raise ValueError("The signature is not authentic (length)")
- flag = int(ph)
- # dom4(flag, self._context)
- dom4 = b'SigEd448' + bchr(flag) + \
- bchr(len(self._context)) + self._context
- PHM = msg_or_hash.read(64) if ph else msg_or_hash
- # Section 5.2.7
- # Step 1
- try:
- R = import_public_key(signature[:57]).pointQ
- except ValueError:
- raise ValueError("The signature is not authentic (R)")
- s = Integer.from_bytes(signature[57:], 'little')
- if s > self._order:
- raise ValueError("The signature is not authentic (S)")
- # Step 2
- k_hash = SHAKE256.new(dom4 + signature[:57] + self._A + PHM).read(114)
- k = Integer.from_bytes(k_hash, 'little') % self._order
- # Step 3
- point1 = s * 8 * self._key._curve.G
- # OPTIMIZE: with double-scalar multiplication, with no SCA
- # countermeasures because it is public values
- point2 = 8 * R + k * 8 * self._key.pointQ
- if point1 != point2:
- raise ValueError("The signature is not authentic")
- def new(key, mode, context=None):
- """Create a signature object :class:`EdDSASigScheme` that
- can perform or verify an EdDSA signature.
- Args:
- key (:class:`Crypto.PublicKey.ECC` object):
- The key to use for computing the signature (*private* keys only)
- or for verifying one.
- The key must be on the curve ``Ed25519`` or ``Ed448``.
- mode (string):
- This parameter must be ``'rfc8032'``.
- context (bytes):
- Up to 255 bytes of `context <https://datatracker.ietf.org/doc/html/rfc8032#page-41>`_,
- which is a constant byte string to segregate different protocols or
- different applications of the same key.
- """
- if not isinstance(key, EccKey) or not key._is_eddsa():
- raise ValueError("EdDSA can only be used with EdDSA keys")
- if mode != 'rfc8032':
- raise ValueError("Mode must be 'rfc8032'")
- if context is None:
- context = b''
- elif len(context) > 255:
- raise ValueError("Context for EdDSA must not be longer than 255 bytes")
- return EdDSASigScheme(key, context)
|