eddsa.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. # ===================================================================
  2. #
  3. # Copyright (c) 2022, Legrandin <helderijs@gmail.com>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. #
  10. # 1. Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # 2. Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in
  14. # the documentation and/or other materials provided with the
  15. # distribution.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  27. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29. # ===================================================================
  30. from Crypto.Math.Numbers import Integer
  31. from Crypto.Hash import SHA512, SHAKE256
  32. from Crypto.Util.py3compat import bchr, is_bytes
  33. from Crypto.PublicKey.ECC import (EccKey,
  34. construct,
  35. _import_ed25519_public_key,
  36. _import_ed448_public_key)
  37. def import_public_key(encoded):
  38. """Create a new Ed25519 or Ed448 public key object,
  39. starting from the key encoded as raw ``bytes``,
  40. in the format described in RFC8032.
  41. Args:
  42. encoded (bytes):
  43. The EdDSA public key to import.
  44. It must be 32 bytes for Ed25519, and 57 bytes for Ed448.
  45. Returns:
  46. :class:`Crypto.PublicKey.EccKey` : a new ECC key object.
  47. Raises:
  48. ValueError: when the given key cannot be parsed.
  49. """
  50. if len(encoded) == 32:
  51. x, y = _import_ed25519_public_key(encoded)
  52. curve_name = "Ed25519"
  53. elif len(encoded) == 57:
  54. x, y = _import_ed448_public_key(encoded)
  55. curve_name = "Ed448"
  56. else:
  57. raise ValueError("Not an EdDSA key (%d bytes)" % len(encoded))
  58. return construct(curve=curve_name, point_x=x, point_y=y)
  59. def import_private_key(encoded):
  60. """Create a new Ed25519 or Ed448 private key object,
  61. starting from the key encoded as raw ``bytes``,
  62. in the format described in RFC8032.
  63. Args:
  64. encoded (bytes):
  65. The EdDSA private key to import.
  66. It must be 32 bytes for Ed25519, and 57 bytes for Ed448.
  67. Returns:
  68. :class:`Crypto.PublicKey.EccKey` : a new ECC key object.
  69. Raises:
  70. ValueError: when the given key cannot be parsed.
  71. """
  72. if len(encoded) == 32:
  73. curve_name = "ed25519"
  74. elif len(encoded) == 57:
  75. curve_name = "ed448"
  76. else:
  77. raise ValueError("Incorrect length. Only EdDSA private keys are supported.")
  78. # Note that the private key is truly a sequence of random bytes,
  79. # so we cannot check its correctness in any way.
  80. return construct(seed=encoded, curve=curve_name)
  81. class EdDSASigScheme(object):
  82. """An EdDSA signature object.
  83. Do not instantiate directly.
  84. Use :func:`Crypto.Signature.eddsa.new`.
  85. """
  86. def __init__(self, key, context):
  87. """Create a new EdDSA object.
  88. Do not instantiate this object directly,
  89. use `Crypto.Signature.DSS.new` instead.
  90. """
  91. self._key = key
  92. self._context = context
  93. self._A = key._export_eddsa()
  94. self._order = key._curve.order
  95. def can_sign(self):
  96. """Return ``True`` if this signature object can be used
  97. for signing messages."""
  98. return self._key.has_private()
  99. def sign(self, msg_or_hash):
  100. """Compute the EdDSA signature of a message.
  101. Args:
  102. msg_or_hash (bytes or a hash object):
  103. The message to sign (``bytes``, in case of *PureEdDSA*) or
  104. the hash that was carried out over the message (hash object, for *HashEdDSA*).
  105. The hash object must be :class:`Crypto.Hash.SHA512` for Ed25519,
  106. and :class:`Crypto.Hash.SHAKE256` object for Ed448.
  107. :return: The signature as ``bytes``. It is always 64 bytes for Ed25519, and 114 bytes for Ed448.
  108. :raise TypeError: if the EdDSA key has no private half
  109. """
  110. if not self._key.has_private():
  111. raise TypeError("Private key is needed to sign")
  112. if self._key._curve.name == "ed25519":
  113. ph = isinstance(msg_or_hash, SHA512.SHA512Hash)
  114. if not (ph or is_bytes(msg_or_hash)):
  115. raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash")
  116. eddsa_sign_method = self._sign_ed25519
  117. elif self._key._curve.name == "ed448":
  118. ph = isinstance(msg_or_hash, SHAKE256.SHAKE256_XOF)
  119. if not (ph or is_bytes(msg_or_hash)):
  120. raise TypeError("'msg_or_hash' must be bytes of a SHAKE256 hash")
  121. eddsa_sign_method = self._sign_ed448
  122. else:
  123. raise ValueError("Incorrect curve for EdDSA")
  124. return eddsa_sign_method(msg_or_hash, ph)
  125. def _sign_ed25519(self, msg_or_hash, ph):
  126. if self._context or ph:
  127. flag = int(ph)
  128. # dom2(flag, self._context)
  129. dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \
  130. bchr(len(self._context)) + self._context
  131. else:
  132. dom2 = b''
  133. PHM = msg_or_hash.digest() if ph else msg_or_hash
  134. # See RFC 8032, section 5.1.6
  135. # Step 2
  136. r_hash = SHA512.new(dom2 + self._key._prefix + PHM).digest()
  137. r = Integer.from_bytes(r_hash, 'little') % self._order
  138. # Step 3
  139. R_pk = EccKey(point=r * self._key._curve.G)._export_eddsa()
  140. # Step 4
  141. k_hash = SHA512.new(dom2 + R_pk + self._A + PHM).digest()
  142. k = Integer.from_bytes(k_hash, 'little') % self._order
  143. # Step 5
  144. s = (r + k * self._key.d) % self._order
  145. return R_pk + s.to_bytes(32, 'little')
  146. def _sign_ed448(self, msg_or_hash, ph):
  147. flag = int(ph)
  148. # dom4(flag, self._context)
  149. dom4 = b'SigEd448' + bchr(flag) + \
  150. bchr(len(self._context)) + self._context
  151. PHM = msg_or_hash.read(64) if ph else msg_or_hash
  152. # See RFC 8032, section 5.2.6
  153. # Step 2
  154. r_hash = SHAKE256.new(dom4 + self._key._prefix + PHM).read(114)
  155. r = Integer.from_bytes(r_hash, 'little') % self._order
  156. # Step 3
  157. R_pk = EccKey(point=r * self._key._curve.G)._export_eddsa()
  158. # Step 4
  159. k_hash = SHAKE256.new(dom4 + R_pk + self._A + PHM).read(114)
  160. k = Integer.from_bytes(k_hash, 'little') % self._order
  161. # Step 5
  162. s = (r + k * self._key.d) % self._order
  163. return R_pk + s.to_bytes(57, 'little')
  164. def verify(self, msg_or_hash, signature):
  165. """Check if an EdDSA signature is authentic.
  166. Args:
  167. msg_or_hash (bytes or a hash object):
  168. The message to verify (``bytes``, in case of *PureEdDSA*) or
  169. the hash that was carried out over the message (hash object, for *HashEdDSA*).
  170. The hash object must be :class:`Crypto.Hash.SHA512` object for Ed25519,
  171. and :class:`Crypto.Hash.SHAKE256` for Ed448.
  172. signature (``bytes``):
  173. The signature that needs to be validated.
  174. It must be 64 bytes for Ed25519, and 114 bytes for Ed448.
  175. :raise ValueError: if the signature is not authentic
  176. """
  177. if self._key._curve.name == "ed25519":
  178. ph = isinstance(msg_or_hash, SHA512.SHA512Hash)
  179. if not (ph or is_bytes(msg_or_hash)):
  180. raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash")
  181. eddsa_verify_method = self._verify_ed25519
  182. elif self._key._curve.name == "ed448":
  183. ph = isinstance(msg_or_hash, SHAKE256.SHAKE256_XOF)
  184. if not (ph or is_bytes(msg_or_hash)):
  185. raise TypeError("'msg_or_hash' must be bytes of a SHAKE256 hash")
  186. eddsa_verify_method = self._verify_ed448
  187. else:
  188. raise ValueError("Incorrect curve for EdDSA")
  189. return eddsa_verify_method(msg_or_hash, signature, ph)
  190. def _verify_ed25519(self, msg_or_hash, signature, ph):
  191. if len(signature) != 64:
  192. raise ValueError("The signature is not authentic (length)")
  193. if self._context or ph:
  194. flag = int(ph)
  195. dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \
  196. bchr(len(self._context)) + self._context
  197. else:
  198. dom2 = b''
  199. PHM = msg_or_hash.digest() if ph else msg_or_hash
  200. # Section 5.1.7
  201. # Step 1
  202. try:
  203. R = import_public_key(signature[:32]).pointQ
  204. except ValueError:
  205. raise ValueError("The signature is not authentic (R)")
  206. s = Integer.from_bytes(signature[32:], 'little')
  207. if s > self._order:
  208. raise ValueError("The signature is not authentic (S)")
  209. # Step 2
  210. k_hash = SHA512.new(dom2 + signature[:32] + self._A + PHM).digest()
  211. k = Integer.from_bytes(k_hash, 'little') % self._order
  212. # Step 3
  213. point1 = s * 8 * self._key._curve.G
  214. # OPTIMIZE: with double-scalar multiplication, with no SCA
  215. # countermeasures because it is public values
  216. point2 = 8 * R + k * 8 * self._key.pointQ
  217. if point1 != point2:
  218. raise ValueError("The signature is not authentic")
  219. def _verify_ed448(self, msg_or_hash, signature, ph):
  220. if len(signature) != 114:
  221. raise ValueError("The signature is not authentic (length)")
  222. flag = int(ph)
  223. # dom4(flag, self._context)
  224. dom4 = b'SigEd448' + bchr(flag) + \
  225. bchr(len(self._context)) + self._context
  226. PHM = msg_or_hash.read(64) if ph else msg_or_hash
  227. # Section 5.2.7
  228. # Step 1
  229. try:
  230. R = import_public_key(signature[:57]).pointQ
  231. except ValueError:
  232. raise ValueError("The signature is not authentic (R)")
  233. s = Integer.from_bytes(signature[57:], 'little')
  234. if s > self._order:
  235. raise ValueError("The signature is not authentic (S)")
  236. # Step 2
  237. k_hash = SHAKE256.new(dom4 + signature[:57] + self._A + PHM).read(114)
  238. k = Integer.from_bytes(k_hash, 'little') % self._order
  239. # Step 3
  240. point1 = s * 8 * self._key._curve.G
  241. # OPTIMIZE: with double-scalar multiplication, with no SCA
  242. # countermeasures because it is public values
  243. point2 = 8 * R + k * 8 * self._key.pointQ
  244. if point1 != point2:
  245. raise ValueError("The signature is not authentic")
  246. def new(key, mode, context=None):
  247. """Create a signature object :class:`EdDSASigScheme` that
  248. can perform or verify an EdDSA signature.
  249. Args:
  250. key (:class:`Crypto.PublicKey.ECC` object):
  251. The key to use for computing the signature (*private* keys only)
  252. or for verifying one.
  253. The key must be on the curve ``Ed25519`` or ``Ed448``.
  254. mode (string):
  255. This parameter must be ``'rfc8032'``.
  256. context (bytes):
  257. Up to 255 bytes of `context <https://datatracker.ietf.org/doc/html/rfc8032#page-41>`_,
  258. which is a constant byte string to segregate different protocols or
  259. different applications of the same key.
  260. """
  261. if not isinstance(key, EccKey) or not key._is_eddsa():
  262. raise ValueError("EdDSA can only be used with EdDSA keys")
  263. if mode != 'rfc8032':
  264. raise ValueError("Mode must be 'rfc8032'")
  265. if context is None:
  266. context = b''
  267. elif len(context) > 255:
  268. raise ValueError("Context for EdDSA must not be longer than 255 bytes")
  269. return EdDSASigScheme(key, context)