crypto_sign.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. # Copyright 2013 Donald Stufft and individual contributors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from typing import Tuple
  15. from nacl import exceptions as exc
  16. from nacl._sodium import ffi, lib
  17. from nacl.exceptions import ensure
  18. crypto_sign_BYTES: int = lib.crypto_sign_bytes()
  19. # crypto_sign_SEEDBYTES = lib.crypto_sign_seedbytes()
  20. crypto_sign_SEEDBYTES: int = lib.crypto_sign_secretkeybytes() // 2
  21. crypto_sign_PUBLICKEYBYTES: int = lib.crypto_sign_publickeybytes()
  22. crypto_sign_SECRETKEYBYTES: int = lib.crypto_sign_secretkeybytes()
  23. crypto_sign_curve25519_BYTES: int = lib.crypto_box_secretkeybytes()
  24. crypto_sign_ed25519ph_STATEBYTES: int = lib.crypto_sign_ed25519ph_statebytes()
  25. def crypto_sign_keypair() -> Tuple[bytes, bytes]:
  26. """
  27. Returns a randomly generated public key and secret key.
  28. :rtype: (bytes(public_key), bytes(secret_key))
  29. """
  30. pk = ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
  31. sk = ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
  32. rc = lib.crypto_sign_keypair(pk, sk)
  33. ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
  34. return (
  35. ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
  36. ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
  37. )
  38. def crypto_sign_seed_keypair(seed: bytes) -> Tuple[bytes, bytes]:
  39. """
  40. Computes and returns the public key and secret key using the seed ``seed``.
  41. :param seed: bytes
  42. :rtype: (bytes(public_key), bytes(secret_key))
  43. """
  44. if len(seed) != crypto_sign_SEEDBYTES:
  45. raise exc.ValueError("Invalid seed")
  46. pk = ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
  47. sk = ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
  48. rc = lib.crypto_sign_seed_keypair(pk, sk, seed)
  49. ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
  50. return (
  51. ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
  52. ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
  53. )
  54. def crypto_sign(message: bytes, sk: bytes) -> bytes:
  55. """
  56. Signs the message ``message`` using the secret key ``sk`` and returns the
  57. signed message.
  58. :param message: bytes
  59. :param sk: bytes
  60. :rtype: bytes
  61. """
  62. signed = ffi.new("unsigned char[]", len(message) + crypto_sign_BYTES)
  63. signed_len = ffi.new("unsigned long long *")
  64. rc = lib.crypto_sign(signed, signed_len, message, len(message), sk)
  65. ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
  66. return ffi.buffer(signed, signed_len[0])[:]
  67. def crypto_sign_open(signed: bytes, pk: bytes) -> bytes:
  68. """
  69. Verifies the signature of the signed message ``signed`` using the public
  70. key ``pk`` and returns the unsigned message.
  71. :param signed: bytes
  72. :param pk: bytes
  73. :rtype: bytes
  74. """
  75. message = ffi.new("unsigned char[]", len(signed))
  76. message_len = ffi.new("unsigned long long *")
  77. if (
  78. lib.crypto_sign_open(message, message_len, signed, len(signed), pk)
  79. != 0
  80. ):
  81. raise exc.BadSignatureError("Signature was forged or corrupt")
  82. return ffi.buffer(message, message_len[0])[:]
  83. def crypto_sign_ed25519_pk_to_curve25519(public_key_bytes: bytes) -> bytes:
  84. """
  85. Converts a public Ed25519 key (encoded as bytes ``public_key_bytes``) to
  86. a public Curve25519 key as bytes.
  87. Raises a ValueError if ``public_key_bytes`` is not of length
  88. ``crypto_sign_PUBLICKEYBYTES``
  89. :param public_key_bytes: bytes
  90. :rtype: bytes
  91. """
  92. if len(public_key_bytes) != crypto_sign_PUBLICKEYBYTES:
  93. raise exc.ValueError("Invalid curve public key")
  94. curve_public_key_len = crypto_sign_curve25519_BYTES
  95. curve_public_key = ffi.new("unsigned char[]", curve_public_key_len)
  96. rc = lib.crypto_sign_ed25519_pk_to_curve25519(
  97. curve_public_key, public_key_bytes
  98. )
  99. ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
  100. return ffi.buffer(curve_public_key, curve_public_key_len)[:]
  101. def crypto_sign_ed25519_sk_to_curve25519(secret_key_bytes: bytes) -> bytes:
  102. """
  103. Converts a secret Ed25519 key (encoded as bytes ``secret_key_bytes``) to
  104. a secret Curve25519 key as bytes.
  105. Raises a ValueError if ``secret_key_bytes``is not of length
  106. ``crypto_sign_SECRETKEYBYTES``
  107. :param secret_key_bytes: bytes
  108. :rtype: bytes
  109. """
  110. if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
  111. raise exc.ValueError("Invalid curve secret key")
  112. curve_secret_key_len = crypto_sign_curve25519_BYTES
  113. curve_secret_key = ffi.new("unsigned char[]", curve_secret_key_len)
  114. rc = lib.crypto_sign_ed25519_sk_to_curve25519(
  115. curve_secret_key, secret_key_bytes
  116. )
  117. ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
  118. return ffi.buffer(curve_secret_key, curve_secret_key_len)[:]
  119. def crypto_sign_ed25519_sk_to_pk(secret_key_bytes: bytes) -> bytes:
  120. """
  121. Extract the public Ed25519 key from a secret Ed25519 key (encoded
  122. as bytes ``secret_key_bytes``).
  123. Raises a ValueError if ``secret_key_bytes``is not of length
  124. ``crypto_sign_SECRETKEYBYTES``
  125. :param secret_key_bytes: bytes
  126. :rtype: bytes
  127. """
  128. if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
  129. raise exc.ValueError("Invalid secret key")
  130. return secret_key_bytes[crypto_sign_SEEDBYTES:]
  131. def crypto_sign_ed25519_sk_to_seed(secret_key_bytes: bytes) -> bytes:
  132. """
  133. Extract the seed from a secret Ed25519 key (encoded
  134. as bytes ``secret_key_bytes``).
  135. Raises a ValueError if ``secret_key_bytes``is not of length
  136. ``crypto_sign_SECRETKEYBYTES``
  137. :param secret_key_bytes: bytes
  138. :rtype: bytes
  139. """
  140. if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
  141. raise exc.ValueError("Invalid secret key")
  142. return secret_key_bytes[:crypto_sign_SEEDBYTES]
  143. class crypto_sign_ed25519ph_state:
  144. """
  145. State object wrapping the sha-512 state used in ed25519ph computation
  146. """
  147. __slots__ = ["state"]
  148. def __init__(self) -> None:
  149. self.state: bytes = ffi.new(
  150. "unsigned char[]", crypto_sign_ed25519ph_STATEBYTES
  151. )
  152. rc = lib.crypto_sign_ed25519ph_init(self.state)
  153. ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
  154. def crypto_sign_ed25519ph_update(
  155. edph: crypto_sign_ed25519ph_state, pmsg: bytes
  156. ) -> None:
  157. """
  158. Update the hash state wrapped in edph
  159. :param edph: the ed25519ph state being updated
  160. :type edph: crypto_sign_ed25519ph_state
  161. :param pmsg: the partial message
  162. :type pmsg: bytes
  163. :rtype: None
  164. """
  165. ensure(
  166. isinstance(edph, crypto_sign_ed25519ph_state),
  167. "edph parameter must be a ed25519ph_state object",
  168. raising=exc.TypeError,
  169. )
  170. ensure(
  171. isinstance(pmsg, bytes),
  172. "pmsg parameter must be a bytes object",
  173. raising=exc.TypeError,
  174. )
  175. rc = lib.crypto_sign_ed25519ph_update(edph.state, pmsg, len(pmsg))
  176. ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
  177. def crypto_sign_ed25519ph_final_create(
  178. edph: crypto_sign_ed25519ph_state, sk: bytes
  179. ) -> bytes:
  180. """
  181. Create a signature for the data hashed in edph
  182. using the secret key sk
  183. :param edph: the ed25519ph state for the data
  184. being signed
  185. :type edph: crypto_sign_ed25519ph_state
  186. :param sk: the ed25519 secret part of the signing key
  187. :type sk: bytes
  188. :return: ed25519ph signature
  189. :rtype: bytes
  190. """
  191. ensure(
  192. isinstance(edph, crypto_sign_ed25519ph_state),
  193. "edph parameter must be a ed25519ph_state object",
  194. raising=exc.TypeError,
  195. )
  196. ensure(
  197. isinstance(sk, bytes),
  198. "secret key parameter must be a bytes object",
  199. raising=exc.TypeError,
  200. )
  201. ensure(
  202. len(sk) == crypto_sign_SECRETKEYBYTES,
  203. ("secret key must be {} bytes long").format(
  204. crypto_sign_SECRETKEYBYTES
  205. ),
  206. raising=exc.TypeError,
  207. )
  208. signature = ffi.new("unsigned char[]", crypto_sign_BYTES)
  209. rc = lib.crypto_sign_ed25519ph_final_create(
  210. edph.state, signature, ffi.NULL, sk
  211. )
  212. ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError)
  213. return ffi.buffer(signature, crypto_sign_BYTES)[:]
  214. def crypto_sign_ed25519ph_final_verify(
  215. edph: crypto_sign_ed25519ph_state, signature: bytes, pk: bytes
  216. ) -> bool:
  217. """
  218. Verify a prehashed signature using the public key pk
  219. :param edph: the ed25519ph state for the data
  220. being verified
  221. :type edph: crypto_sign_ed25519ph_state
  222. :param signature: the signature being verified
  223. :type signature: bytes
  224. :param pk: the ed25519 public part of the signing key
  225. :type pk: bytes
  226. :return: True if the signature is valid
  227. :rtype: boolean
  228. :raises exc.BadSignatureError: if the signature is not valid
  229. """
  230. ensure(
  231. isinstance(edph, crypto_sign_ed25519ph_state),
  232. "edph parameter must be a ed25519ph_state object",
  233. raising=exc.TypeError,
  234. )
  235. ensure(
  236. isinstance(signature, bytes),
  237. "signature parameter must be a bytes object",
  238. raising=exc.TypeError,
  239. )
  240. ensure(
  241. len(signature) == crypto_sign_BYTES,
  242. ("signature must be {} bytes long").format(crypto_sign_BYTES),
  243. raising=exc.TypeError,
  244. )
  245. ensure(
  246. isinstance(pk, bytes),
  247. "public key parameter must be a bytes object",
  248. raising=exc.TypeError,
  249. )
  250. ensure(
  251. len(pk) == crypto_sign_PUBLICKEYBYTES,
  252. ("public key must be {} bytes long").format(
  253. crypto_sign_PUBLICKEYBYTES
  254. ),
  255. raising=exc.TypeError,
  256. )
  257. rc = lib.crypto_sign_ed25519ph_final_verify(edph.state, signature, pk)
  258. if rc != 0:
  259. raise exc.BadSignatureError("Signature was forged or corrupt")
  260. return True