crypto_generichash.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. # Copyright 2013-2019 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 NoReturn, TypeVar
  15. from nacl import exceptions as exc
  16. from nacl._sodium import ffi, lib
  17. from nacl.exceptions import ensure
  18. crypto_generichash_BYTES: int = lib.crypto_generichash_blake2b_bytes()
  19. crypto_generichash_BYTES_MIN: int = lib.crypto_generichash_blake2b_bytes_min()
  20. crypto_generichash_BYTES_MAX: int = lib.crypto_generichash_blake2b_bytes_max()
  21. crypto_generichash_KEYBYTES: int = lib.crypto_generichash_blake2b_keybytes()
  22. crypto_generichash_KEYBYTES_MIN: int = (
  23. lib.crypto_generichash_blake2b_keybytes_min()
  24. )
  25. crypto_generichash_KEYBYTES_MAX: int = (
  26. lib.crypto_generichash_blake2b_keybytes_max()
  27. )
  28. crypto_generichash_SALTBYTES: int = lib.crypto_generichash_blake2b_saltbytes()
  29. crypto_generichash_PERSONALBYTES: int = (
  30. lib.crypto_generichash_blake2b_personalbytes()
  31. )
  32. crypto_generichash_STATEBYTES: int = lib.crypto_generichash_statebytes()
  33. _OVERLONG = "{0} length greater than {1} bytes"
  34. _TOOBIG = "{0} greater than {1}"
  35. def _checkparams(
  36. digest_size: int, key: bytes, salt: bytes, person: bytes
  37. ) -> None:
  38. """Check hash parameters"""
  39. ensure(
  40. isinstance(key, bytes),
  41. "Key must be a bytes sequence",
  42. raising=exc.TypeError,
  43. )
  44. ensure(
  45. isinstance(salt, bytes),
  46. "Salt must be a bytes sequence",
  47. raising=exc.TypeError,
  48. )
  49. ensure(
  50. isinstance(person, bytes),
  51. "Person must be a bytes sequence",
  52. raising=exc.TypeError,
  53. )
  54. ensure(
  55. isinstance(digest_size, int),
  56. "Digest size must be an integer number",
  57. raising=exc.TypeError,
  58. )
  59. ensure(
  60. digest_size <= crypto_generichash_BYTES_MAX,
  61. _TOOBIG.format("Digest_size", crypto_generichash_BYTES_MAX),
  62. raising=exc.ValueError,
  63. )
  64. ensure(
  65. len(key) <= crypto_generichash_KEYBYTES_MAX,
  66. _OVERLONG.format("Key", crypto_generichash_KEYBYTES_MAX),
  67. raising=exc.ValueError,
  68. )
  69. ensure(
  70. len(salt) <= crypto_generichash_SALTBYTES,
  71. _OVERLONG.format("Salt", crypto_generichash_SALTBYTES),
  72. raising=exc.ValueError,
  73. )
  74. ensure(
  75. len(person) <= crypto_generichash_PERSONALBYTES,
  76. _OVERLONG.format("Person", crypto_generichash_PERSONALBYTES),
  77. raising=exc.ValueError,
  78. )
  79. def generichash_blake2b_salt_personal(
  80. data: bytes,
  81. digest_size: int = crypto_generichash_BYTES,
  82. key: bytes = b"",
  83. salt: bytes = b"",
  84. person: bytes = b"",
  85. ) -> bytes:
  86. """One shot hash interface
  87. :param data: the input data to the hash function
  88. :type data: bytes
  89. :param digest_size: must be at most
  90. :py:data:`.crypto_generichash_BYTES_MAX`;
  91. the default digest size is
  92. :py:data:`.crypto_generichash_BYTES`
  93. :type digest_size: int
  94. :param key: must be at most
  95. :py:data:`.crypto_generichash_KEYBYTES_MAX` long
  96. :type key: bytes
  97. :param salt: must be at most
  98. :py:data:`.crypto_generichash_SALTBYTES` long;
  99. will be zero-padded if needed
  100. :type salt: bytes
  101. :param person: must be at most
  102. :py:data:`.crypto_generichash_PERSONALBYTES` long:
  103. will be zero-padded if needed
  104. :type person: bytes
  105. :return: digest_size long digest
  106. :rtype: bytes
  107. """
  108. _checkparams(digest_size, key, salt, person)
  109. ensure(
  110. isinstance(data, bytes),
  111. "Input data must be a bytes sequence",
  112. raising=exc.TypeError,
  113. )
  114. digest = ffi.new("unsigned char[]", digest_size)
  115. # both _salt and _personal must be zero-padded to the correct length
  116. _salt = ffi.new("unsigned char []", crypto_generichash_SALTBYTES)
  117. _person = ffi.new("unsigned char []", crypto_generichash_PERSONALBYTES)
  118. ffi.memmove(_salt, salt, len(salt))
  119. ffi.memmove(_person, person, len(person))
  120. rc = lib.crypto_generichash_blake2b_salt_personal(
  121. digest, digest_size, data, len(data), key, len(key), _salt, _person
  122. )
  123. ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
  124. return ffi.buffer(digest, digest_size)[:]
  125. _Blake2State = TypeVar("_Blake2State", bound="Blake2State")
  126. class Blake2State:
  127. """
  128. Python-level wrapper for the crypto_generichash_blake2b state buffer
  129. """
  130. __slots__ = ["_statebuf", "digest_size"]
  131. def __init__(self, digest_size: int):
  132. self._statebuf = ffi.new(
  133. "unsigned char[]", crypto_generichash_STATEBYTES
  134. )
  135. self.digest_size = digest_size
  136. def __reduce__(self) -> NoReturn:
  137. """
  138. Raise the same exception as hashlib's blake implementation
  139. on copy.copy()
  140. """
  141. raise TypeError(
  142. "can't pickle {} objects".format(self.__class__.__name__)
  143. )
  144. def copy(self: _Blake2State) -> _Blake2State:
  145. _st = self.__class__(self.digest_size)
  146. ffi.memmove(
  147. _st._statebuf, self._statebuf, crypto_generichash_STATEBYTES
  148. )
  149. return _st
  150. def generichash_blake2b_init(
  151. key: bytes = b"",
  152. salt: bytes = b"",
  153. person: bytes = b"",
  154. digest_size: int = crypto_generichash_BYTES,
  155. ) -> Blake2State:
  156. """
  157. Create a new initialized blake2b hash state
  158. :param key: must be at most
  159. :py:data:`.crypto_generichash_KEYBYTES_MAX` long
  160. :type key: bytes
  161. :param salt: must be at most
  162. :py:data:`.crypto_generichash_SALTBYTES` long;
  163. will be zero-padded if needed
  164. :type salt: bytes
  165. :param person: must be at most
  166. :py:data:`.crypto_generichash_PERSONALBYTES` long:
  167. will be zero-padded if needed
  168. :type person: bytes
  169. :param digest_size: must be at most
  170. :py:data:`.crypto_generichash_BYTES_MAX`;
  171. the default digest size is
  172. :py:data:`.crypto_generichash_BYTES`
  173. :type digest_size: int
  174. :return: a initialized :py:class:`.Blake2State`
  175. :rtype: object
  176. """
  177. _checkparams(digest_size, key, salt, person)
  178. state = Blake2State(digest_size)
  179. # both _salt and _personal must be zero-padded to the correct length
  180. _salt = ffi.new("unsigned char []", crypto_generichash_SALTBYTES)
  181. _person = ffi.new("unsigned char []", crypto_generichash_PERSONALBYTES)
  182. ffi.memmove(_salt, salt, len(salt))
  183. ffi.memmove(_person, person, len(person))
  184. rc = lib.crypto_generichash_blake2b_init_salt_personal(
  185. state._statebuf, key, len(key), digest_size, _salt, _person
  186. )
  187. ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
  188. return state
  189. def generichash_blake2b_update(state: Blake2State, data: bytes) -> None:
  190. """Update the blake2b hash state
  191. :param state: a initialized Blake2bState object as returned from
  192. :py:func:`.crypto_generichash_blake2b_init`
  193. :type state: :py:class:`.Blake2State`
  194. :param data:
  195. :type data: bytes
  196. """
  197. ensure(
  198. isinstance(state, Blake2State),
  199. "State must be a Blake2State object",
  200. raising=exc.TypeError,
  201. )
  202. ensure(
  203. isinstance(data, bytes),
  204. "Input data must be a bytes sequence",
  205. raising=exc.TypeError,
  206. )
  207. rc = lib.crypto_generichash_blake2b_update(
  208. state._statebuf, data, len(data)
  209. )
  210. ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
  211. def generichash_blake2b_final(state: Blake2State) -> bytes:
  212. """Finalize the blake2b hash state and return the digest.
  213. :param state: a initialized Blake2bState object as returned from
  214. :py:func:`.crypto_generichash_blake2b_init`
  215. :type state: :py:class:`.Blake2State`
  216. :return: the blake2 digest of the passed-in data stream
  217. :rtype: bytes
  218. """
  219. ensure(
  220. isinstance(state, Blake2State),
  221. "State must be a Blake2State object",
  222. raising=exc.TypeError,
  223. )
  224. _digest = ffi.new("unsigned char[]", crypto_generichash_BYTES_MAX)
  225. rc = lib.crypto_generichash_blake2b_final(
  226. state._statebuf, _digest, state.digest_size
  227. )
  228. ensure(rc == 0, "Unexpected failure", raising=exc.RuntimeError)
  229. return ffi.buffer(_digest, state.digest_size)[:]