| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  | # =================================================================== | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright (c) 2014, 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 binascii import unhexlify | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  | from Crypto.Util.py3compat import bord, tobytes | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | from Crypto.Random import get_random_bytes | 
					
						
							|  |  |  | from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, | 
					
						
							|  |  |  |                                   VoidPointer, SmartPointer, | 
					
						
							|  |  |  |                                   create_string_buffer, | 
					
						
							|  |  |  |                                   get_raw_buffer, c_size_t, | 
					
						
							| 
									
										
										
										
											2018-03-06 13:48:00 +01:00
										 |  |  |                                   c_uint8_ptr) | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | _raw_blake2s_lib = load_pycryptodome_raw_lib("Crypto.Hash._BLAKE2s", | 
					
						
							|  |  |  |                         """
 | 
					
						
							|  |  |  |                         int blake2s_init(void **state, | 
					
						
							|  |  |  |                                          const uint8_t *key, | 
					
						
							|  |  |  |                                          size_t key_size, | 
					
						
							|  |  |  |                                          size_t digest_size); | 
					
						
							|  |  |  |                         int blake2s_destroy(void *state); | 
					
						
							|  |  |  |                         int blake2s_update(void *state, | 
					
						
							|  |  |  |                                            const uint8_t *buf, | 
					
						
							|  |  |  |                                            size_t len); | 
					
						
							|  |  |  |                         int blake2s_digest(const void *state, | 
					
						
							|  |  |  |                                            uint8_t digest[32]); | 
					
						
							|  |  |  |                         int blake2s_copy(const void *src, void *dst); | 
					
						
							|  |  |  |                         """)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BLAKE2s_Hash(object): | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |     """A BLAKE2s hash object.
 | 
					
						
							|  |  |  |     Do not instantiate directly. Use the :func:`new` function. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :ivar oid: ASN.1 Object ID | 
					
						
							|  |  |  |     :vartype oid: string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :ivar block_size: the size in bytes of the internal message block, | 
					
						
							|  |  |  |                       input to the compression function | 
					
						
							|  |  |  |     :vartype block_size: integer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :ivar digest_size: the size in bytes of the resulting hash | 
					
						
							|  |  |  |     :vartype digest_size: integer | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |     # The internal block size of the hash algorithm in bytes. | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |     block_size = 32 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-02 02:47:58 -05:00
										 |  |  |     def __init__(self, data, key, digest_bytes, update_after_digest): | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         # The size of the resulting hash in bytes. | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |         self.digest_size = digest_bytes | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-02 02:47:58 -05:00
										 |  |  |         self._update_after_digest = update_after_digest | 
					
						
							|  |  |  |         self._digest_done = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         # See https://tools.ietf.org/html/rfc7693 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |         if digest_bytes in (16, 20, 28, 32) and not key: | 
					
						
							|  |  |  |             self.oid = "1.3.6.1.4.1.1722.12.2.2." + str(digest_bytes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         state = VoidPointer() | 
					
						
							|  |  |  |         result = _raw_blake2s_lib.blake2s_init(state.address_of(), | 
					
						
							| 
									
										
										
										
											2018-03-06 13:48:00 +01:00
										 |  |  |                                                c_uint8_ptr(key), | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |                                                c_size_t(len(key)), | 
					
						
							|  |  |  |                                                c_size_t(digest_bytes) | 
					
						
							|  |  |  |                                                ) | 
					
						
							|  |  |  |         if result: | 
					
						
							|  |  |  |             raise ValueError("Error %d while instantiating BLAKE2s" % result) | 
					
						
							|  |  |  |         self._state = SmartPointer(state.get(), | 
					
						
							|  |  |  |                                    _raw_blake2s_lib.blake2s_destroy) | 
					
						
							|  |  |  |         if data: | 
					
						
							|  |  |  |             self.update(data) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |     def update(self, data): | 
					
						
							|  |  |  |         """Continue hashing of a message by consuming the next chunk of data.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2018-04-01 21:47:11 +02:00
										 |  |  |             data (byte string/byte array/memoryview): The next chunk of the message being hashed. | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-02 02:47:58 -05:00
										 |  |  |         if self._digest_done and not self._update_after_digest: | 
					
						
							|  |  |  |             raise TypeError("You can only call 'digest' or 'hexdigest' on this object") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |         result = _raw_blake2s_lib.blake2s_update(self._state.get(), | 
					
						
							| 
									
										
										
										
											2018-03-06 13:48:00 +01:00
										 |  |  |                                                  c_uint8_ptr(data), | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |                                                  c_size_t(len(data))) | 
					
						
							|  |  |  |         if result: | 
					
						
							|  |  |  |             raise ValueError("Error %d while hashing BLAKE2s data" % result) | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |     def digest(self): | 
					
						
							|  |  |  |         """Return the **binary** (non-printable) digest of the message that has been hashed so far.
 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         :return: The hash digest, computed over the data processed so far. | 
					
						
							|  |  |  |                  Binary form. | 
					
						
							|  |  |  |         :rtype: byte string | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bfr = create_string_buffer(32) | 
					
						
							|  |  |  |         result = _raw_blake2s_lib.blake2s_digest(self._state.get(), | 
					
						
							|  |  |  |                                                  bfr) | 
					
						
							|  |  |  |         if result: | 
					
						
							|  |  |  |             raise ValueError("Error %d while creating BLAKE2s digest" % result) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-02 02:47:58 -05:00
										 |  |  |         self._digest_done = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |         return get_raw_buffer(bfr)[:self.digest_size] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |     def hexdigest(self): | 
					
						
							|  |  |  |         """Return the **printable** digest of the message that has been hashed so far.
 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         :return: The hash digest, computed over the data processed so far. | 
					
						
							|  |  |  |                  Hexadecimal encoded. | 
					
						
							|  |  |  |         :rtype: string | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |     def verify(self, mac_tag): | 
					
						
							|  |  |  |         """Verify that a given **binary** MAC (computed by another party)
 | 
					
						
							|  |  |  |         is valid. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2018-04-01 21:47:11 +02:00
										 |  |  |           mac_tag (byte string/byte array/memoryview): the expected MAC of the message. | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Raises: | 
					
						
							|  |  |  |             ValueError: if the MAC does not match. It means that the message | 
					
						
							|  |  |  |                 has been tampered with or that the MAC key is incorrect. | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         secret = get_random_bytes(16) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         mac1 = new(digest_bits=160, key=secret, data=mac_tag) | 
					
						
							|  |  |  |         mac2 = new(digest_bits=160, key=secret, data=self.digest()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if mac1.digest() != mac2.digest(): | 
					
						
							|  |  |  |             raise ValueError("MAC check failed") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |     def hexverify(self, hex_mac_tag): | 
					
						
							|  |  |  |         """Verify that a given **printable** MAC (computed by another party)
 | 
					
						
							|  |  |  |         is valid. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Raises: | 
					
						
							|  |  |  |             ValueError: if the MAC does not match. It means that the message | 
					
						
							|  |  |  |                 has been tampered with or that the MAC key is incorrect. | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.verify(unhexlify(tobytes(hex_mac_tag))) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |     def new(self, **kwargs): | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         """Return a new instance of a BLAKE2s hash object.
 | 
					
						
							|  |  |  |         See :func:`new`. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if "digest_bytes" not in kwargs and "digest_bits" not in kwargs: | 
					
						
							|  |  |  |             kwargs["digest_bytes"] = self.digest_size | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return new(**kwargs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def new(**kwargs): | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |     """Create a new hash object.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							| 
									
										
										
										
											2018-04-01 21:47:11 +02:00
										 |  |  |         data (byte string/byte array/memoryview): | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |             Optional. The very first chunk of the message to hash. | 
					
						
							|  |  |  |             It is equivalent to an early call to :meth:`BLAKE2s_Hash.update`. | 
					
						
							|  |  |  |         digest_bytes (integer): | 
					
						
							| 
									
										
										
										
											2018-03-30 22:09:04 +02:00
										 |  |  |             Optional. The size of the digest, in bytes (1 to 32). Default is 32. | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         digest_bits (integer): | 
					
						
							| 
									
										
										
										
											2018-03-30 22:09:04 +02:00
										 |  |  |             Optional and alternative to ``digest_bytes``. | 
					
						
							|  |  |  |             The size of the digest, in bits (8 to 256, in steps of 8). | 
					
						
							|  |  |  |             Default is 256. | 
					
						
							| 
									
										
										
										
											2017-08-10 23:54:32 +02:00
										 |  |  |         key (byte string): | 
					
						
							|  |  |  |             Optional. The key to use to compute the MAC (1 to 64 bytes). | 
					
						
							|  |  |  |             If not specified, no key will be used. | 
					
						
							|  |  |  |         update_after_digest (boolean): | 
					
						
							|  |  |  |             Optional. By default, a hash object cannot be updated anymore after | 
					
						
							|  |  |  |             the digest is computed. When this flag is ``True``, such check | 
					
						
							|  |  |  |             is no longer enforced. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							|  |  |  |         A :class:`BLAKE2s_Hash` hash object | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     data = kwargs.pop("data", None) | 
					
						
							| 
									
										
										
										
											2015-11-02 02:47:58 -05:00
										 |  |  |     update_after_digest = kwargs.pop("update_after_digest", False) | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     digest_bytes = kwargs.pop("digest_bytes", None) | 
					
						
							|  |  |  |     digest_bits = kwargs.pop("digest_bits", None) | 
					
						
							|  |  |  |     if None not in (digest_bytes, digest_bits): | 
					
						
							|  |  |  |         raise TypeError("Only one digest parameter must be provided") | 
					
						
							|  |  |  |     if (None, None) == (digest_bytes, digest_bits): | 
					
						
							| 
									
										
										
										
											2018-03-30 22:09:04 +02:00
										 |  |  |         digest_bytes = 32 | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |     if digest_bytes is not None: | 
					
						
							|  |  |  |         if not (1 <= digest_bytes <= 32): | 
					
						
							|  |  |  |             raise ValueError("'digest_bytes' not in range 1..32") | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if not (8 <= digest_bits <= 256) or (digest_bits % 8): | 
					
						
							| 
									
										
										
										
											2022-08-03 11:14:53 +05:30
										 |  |  |             raise ValueError("'digest_bits' not in range 8..256, " | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |                              "with steps of 8") | 
					
						
							|  |  |  |         digest_bytes = digest_bits // 8 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |     key = kwargs.pop("key", b"") | 
					
						
							| 
									
										
										
										
											2015-03-21 22:01:36 +01:00
										 |  |  |     if len(key) > 32: | 
					
						
							|  |  |  |         raise ValueError("BLAKE2s key cannot exceed 32 bytes") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if kwargs: | 
					
						
							|  |  |  |         raise TypeError("Unknown parameters: " + str(kwargs)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-02 02:47:58 -05:00
										 |  |  |     return BLAKE2s_Hash(data, key, digest_bytes, update_after_digest) |