Poly1305.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. # -*- coding: utf-8 -*-
  2. #
  3. # Hash/Poly1305.py - Implements the Poly1305 MAC
  4. #
  5. # ===================================================================
  6. # The contents of this file are dedicated to the public domain. To
  7. # the extent that dedication to the public domain is not available,
  8. # everyone is granted a worldwide, perpetual, royalty-free,
  9. # non-exclusive license to exercise all rights associated with the
  10. # contents of this file for any purpose whatsoever.
  11. # No rights are reserved.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  14. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  15. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  16. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  17. # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  18. # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  19. # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. # SOFTWARE.
  21. # ===================================================================
  22. from binascii import unhexlify
  23. from Cryptodome.Util.py3compat import bord, tobytes, _copy_bytes
  24. from Cryptodome.Hash import BLAKE2s
  25. from Cryptodome.Random import get_random_bytes
  26. from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib,
  27. VoidPointer, SmartPointer,
  28. create_string_buffer,
  29. get_raw_buffer, c_size_t,
  30. c_uint8_ptr)
  31. _raw_poly1305 = load_pycryptodome_raw_lib("Cryptodome.Hash._poly1305",
  32. """
  33. int poly1305_init(void **state,
  34. const uint8_t *r,
  35. size_t r_len,
  36. const uint8_t *s,
  37. size_t s_len);
  38. int poly1305_destroy(void *state);
  39. int poly1305_update(void *state,
  40. const uint8_t *in,
  41. size_t len);
  42. int poly1305_digest(const void *state,
  43. uint8_t *digest,
  44. size_t len);
  45. """)
  46. class Poly1305_MAC(object):
  47. digest_size = 16
  48. def __init__(self, r, s, data):
  49. if len(r) != 16:
  50. raise ValueError("Paramater r is not 16 bytes long")
  51. if len(s) != 16:
  52. raise ValueError("Parameter s is not 16 bytes long")
  53. self._mac_tag = None
  54. state = VoidPointer()
  55. result = _raw_poly1305.poly1305_init(state.address_of(),
  56. c_uint8_ptr(r),
  57. c_size_t(len(r)),
  58. c_uint8_ptr(s),
  59. c_size_t(len(s))
  60. )
  61. if result:
  62. raise ValueError("Error %d while instantiating Poly1305" % result)
  63. self._state = SmartPointer(state.get(),
  64. _raw_poly1305.poly1305_destroy)
  65. if data:
  66. self.update(data)
  67. def update(self, data):
  68. if self._mac_tag:
  69. raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
  70. result = _raw_poly1305.poly1305_update(self._state.get(),
  71. c_uint8_ptr(data),
  72. c_size_t(len(data)))
  73. if result:
  74. raise ValueError("Error %d while hashing Poly1305 data" % result)
  75. return self
  76. def copy(self):
  77. raise NotImplementedError()
  78. def digest(self):
  79. if self._mac_tag:
  80. return self._mac_tag
  81. bfr = create_string_buffer(16)
  82. result = _raw_poly1305.poly1305_digest(self._state.get(),
  83. bfr,
  84. c_size_t(len(bfr)))
  85. if result:
  86. raise ValueError("Error %d while creating Poly1305 digest" % result)
  87. self._mac_tag = get_raw_buffer(bfr)
  88. return self._mac_tag
  89. def hexdigest(self):
  90. return "".join(["%02x" % bord(x)
  91. for x in tuple(self.digest())])
  92. def verify(self, mac_tag):
  93. secret = get_random_bytes(16)
  94. mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag)
  95. mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest())
  96. if mac1.digest() != mac2.digest():
  97. raise ValueError("MAC check failed")
  98. def hexverify(self, hex_mac_tag):
  99. self.verify(unhexlify(tobytes(hex_mac_tag)))
  100. def new(**kwargs):
  101. """Create a new Poly1305 MAC object.
  102. Args:
  103. key (bytes/bytearray/memoryview):
  104. The 32-byte key for the Poly1305 object.
  105. cipher (module from ``Cryptodome.Cipher``):
  106. The cipher algorithm to use for deriving the Poly1305
  107. key pair *(r, s)*.
  108. It can only be ``Cryptodome.Cipher.AES`` or ``Cryptodome.Cipher.ChaCha20``.
  109. nonce (bytes/bytearray/memoryview):
  110. Optional. The non-repeatable value to use for the MAC of this message.
  111. It must be 16 bytes long for ``AES`` and 8 or 12 bytes for ``ChaCha20``.
  112. If not passed, a random nonce is created; you will find it in the
  113. ``nonce`` attribute of the new object.
  114. data (bytes/bytearray/memoryview):
  115. Optional. The very first chunk of the message to authenticate.
  116. It is equivalent to an early call to ``update()``.
  117. Returns:
  118. A :class:`Poly1305_MAC` object
  119. """
  120. cipher = kwargs.pop("cipher", None)
  121. if not hasattr(cipher, '_derive_Poly1305_key_pair'):
  122. raise ValueError("Parameter 'cipher' must be AES or ChaCha20")
  123. cipher_key = kwargs.pop("key", None)
  124. if cipher_key is None:
  125. raise TypeError("You must pass a parameter 'key'")
  126. nonce = kwargs.pop("nonce", None)
  127. data = kwargs.pop("data", None)
  128. if kwargs:
  129. raise TypeError("Unknown parameters: " + str(kwargs))
  130. r, s, nonce = cipher._derive_Poly1305_key_pair(cipher_key, nonce)
  131. new_mac = Poly1305_MAC(r, s, data)
  132. new_mac.nonce = _copy_bytes(None, None, nonce) # nonce may still be just a memoryview
  133. return new_mac