1# 2# HMAC.py - Implements the HMAC algorithm as described by RFC 2104. 3# 4# =================================================================== 5# 6# Copyright (c) 2014, Legrandin <helderijs@gmail.com> 7# All rights reserved. 8# 9# Redistribution and use in source and binary forms, with or without 10# modification, are permitted provided that the following conditions 11# are met: 12# 13# 1. Redistributions of source code must retain the above copyright 14# notice, this list of conditions and the following disclaimer. 15# 2. Redistributions in binary form must reproduce the above copyright 16# notice, this list of conditions and the following disclaimer in 17# the documentation and/or other materials provided with the 18# distribution. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31# POSSIBILITY OF SUCH DAMAGE. 32# =================================================================== 33 34from Crypto.Util.py3compat import bord, tobytes 35 36from binascii import unhexlify 37 38from Crypto.Hash import MD5 39from Crypto.Hash import BLAKE2s 40from Crypto.Util.strxor import strxor 41from Crypto.Random import get_random_bytes 42 43__all__ = ['new', 'HMAC'] 44 45 46class HMAC(object): 47 """An HMAC hash object. 48 Do not instantiate directly. Use the :func:`new` function. 49 50 :ivar digest_size: the size in bytes of the resulting MAC tag 51 :vartype digest_size: integer 52 """ 53 54 def __init__(self, key, msg=b"", digestmod=None): 55 56 if digestmod is None: 57 digestmod = MD5 58 59 if msg is None: 60 msg = b"" 61 62 # Size of the MAC tag 63 self.digest_size = digestmod.digest_size 64 65 self._digestmod = digestmod 66 67 if isinstance(key, memoryview): 68 key = key.tobytes() 69 70 try: 71 if len(key) <= digestmod.block_size: 72 # Step 1 or 2 73 key_0 = key + b"\x00" * (digestmod.block_size - len(key)) 74 else: 75 # Step 3 76 hash_k = digestmod.new(key).digest() 77 key_0 = hash_k + b"\x00" * (digestmod.block_size - len(hash_k)) 78 except AttributeError: 79 # Not all hash types have "block_size" 80 raise ValueError("Hash type incompatible to HMAC") 81 82 # Step 4 83 key_0_ipad = strxor(key_0, b"\x36" * len(key_0)) 84 85 # Start step 5 and 6 86 self._inner = digestmod.new(key_0_ipad) 87 self._inner.update(msg) 88 89 # Step 7 90 key_0_opad = strxor(key_0, b"\x5c" * len(key_0)) 91 92 # Start step 8 and 9 93 self._outer = digestmod.new(key_0_opad) 94 95 def update(self, msg): 96 """Authenticate the next chunk of message. 97 98 Args: 99 data (byte string/byte array/memoryview): The next chunk of data 100 """ 101 102 self._inner.update(msg) 103 return self 104 105 def _pbkdf2_hmac_assist(self, first_digest, iterations): 106 """Carry out the expensive inner loop for PBKDF2-HMAC""" 107 108 result = self._digestmod._pbkdf2_hmac_assist( 109 self._inner, 110 self._outer, 111 first_digest, 112 iterations) 113 return result 114 115 def copy(self): 116 """Return a copy ("clone") of the HMAC object. 117 118 The copy will have the same internal state as the original HMAC 119 object. 120 This can be used to efficiently compute the MAC tag of byte 121 strings that share a common initial substring. 122 123 :return: An :class:`HMAC` 124 """ 125 126 new_hmac = HMAC(b"fake key", digestmod=self._digestmod) 127 128 # Syncronize the state 129 new_hmac._inner = self._inner.copy() 130 new_hmac._outer = self._outer.copy() 131 132 return new_hmac 133 134 def digest(self): 135 """Return the **binary** (non-printable) MAC tag of the message 136 authenticated so far. 137 138 :return: The MAC tag digest, computed over the data processed so far. 139 Binary form. 140 :rtype: byte string 141 """ 142 143 frozen_outer_hash = self._outer.copy() 144 frozen_outer_hash.update(self._inner.digest()) 145 return frozen_outer_hash.digest() 146 147 def verify(self, mac_tag): 148 """Verify that a given **binary** MAC (computed by another party) 149 is valid. 150 151 Args: 152 mac_tag (byte string/byte string/memoryview): the expected MAC of the message. 153 154 Raises: 155 ValueError: if the MAC does not match. It means that the message 156 has been tampered with or that the MAC key is incorrect. 157 """ 158 159 secret = get_random_bytes(16) 160 161 mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag) 162 mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest()) 163 164 if mac1.digest() != mac2.digest(): 165 raise ValueError("MAC check failed") 166 167 def hexdigest(self): 168 """Return the **printable** MAC tag of the message authenticated so far. 169 170 :return: The MAC tag, computed over the data processed so far. 171 Hexadecimal encoded. 172 :rtype: string 173 """ 174 175 return "".join(["%02x" % bord(x) 176 for x in tuple(self.digest())]) 177 178 def hexverify(self, hex_mac_tag): 179 """Verify that a given **printable** MAC (computed by another party) 180 is valid. 181 182 Args: 183 hex_mac_tag (string): the expected MAC of the message, 184 as a hexadecimal string. 185 186 Raises: 187 ValueError: if the MAC does not match. It means that the message 188 has been tampered with or that the MAC key is incorrect. 189 """ 190 191 self.verify(unhexlify(tobytes(hex_mac_tag))) 192 193 194def new(key, msg=b"", digestmod=None): 195 """Create a new MAC object. 196 197 Args: 198 key (bytes/bytearray/memoryview): 199 key for the MAC object. 200 It must be long enough to match the expected security level of the 201 MAC. 202 msg (bytes/bytearray/memoryview): 203 Optional. The very first chunk of the message to authenticate. 204 It is equivalent to an early call to :meth:`HMAC.update`. 205 digestmod (module): 206 The hash to use to implement the HMAC. 207 Default is :mod:`Crypto.Hash.MD5`. 208 209 Returns: 210 An :class:`HMAC` object 211 """ 212 213 return HMAC(key, msg, digestmod) 214