Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/Crypto/Hash/CMAC.py: 63%
122 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 07:03 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 07:03 +0000
1# -*- coding: utf-8 -*-
2#
3# Hash/CMAC.py - Implements the CMAC algorithm
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# ===================================================================
23"""CMAC (Cipher-based Message Authentication Code) algorithm
25CMAC is a MAC defined in `NIST SP 800-38B`_ and in RFC4493_ (for AES only)
26and constructed using a block cipher. It was originally known as `OMAC1`_.
28The algorithm is sometimes named *X-CMAC* where *X* is the name
29of the cipher (e.g. AES-CMAC).
31This is an example showing how to *create* an AES-CMAC:
33 >>> from Crypto.Hash import CMAC
34 >>> from Crypto.Cipher import AES
35 >>>
36 >>> secret = b'Sixteen byte key'
37 >>> cobj = CMAC.new(secret, ciphermod=AES)
38 >>> cobj.update(b'Hello')
39 >>> print cobj.hexdigest()
41And this is an example showing how to *check* an AES-CMAC:
43 >>> from Crypto.Hash import CMAC
44 >>> from Crypto.Cipher import AES
45 >>>
46 >>> # We have received a message 'msg' together
47 >>> # with its MAC 'mac'
48 >>>
49 >>> secret = b'Sixteen byte key'
50 >>> cobj = CMAC.new(secret, ciphermod=AES)
51 >>> cobj.update(msg)
52 >>> try:
53 >>> cobj.verify(mac)
54 >>> print "The message '%s' is authentic" % msg
55 >>> except ValueError:
56 >>> print "The message or the key is wrong"
58.. _`NIST SP 800-38B`: http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf
59.. _RFC4493: http://www.ietf.org/rfc/rfc4493.txt
60.. _OMAC1: http://www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
61"""
63__all__ = ['new', 'digest_size', 'CMAC' ]
65import sys
66if sys.version_info[0] == 2 and sys.version_info[1] == 1:
67 from Crypto.Util.py21compat import *
68from Crypto.Util.py3compat import *
70from binascii import unhexlify
72from Crypto.Util.strxor import strxor
73from Crypto.Util.number import long_to_bytes, bytes_to_long
75#: The size of the authentication tag produced by the MAC.
76digest_size = None
78def _shift_bytes(bs, xor_lsb=0):
79 num = (bytes_to_long(bs)<<1) ^ xor_lsb
80 return long_to_bytes(num, len(bs))[-len(bs):]
82class _SmoothMAC(object):
83 """Turn a MAC that only operates on aligned blocks of data
84 into a MAC with granularity of 1 byte."""
86 def __init__(self, block_size, msg=b(""), min_digest=0):
87 self._bs = block_size
88 #: Data waiting to be MAC-ed
89 self._buffer = []
90 self._buffer_len = 0
91 #: Data received via update()
92 self._total_len = 0
93 #: Minimum amount of bytes required by the final digest step
94 self._min_digest = min_digest
95 #: Block MAC object
96 self._mac = None
97 #: Cached digest
98 self._tag = None
99 if msg:
100 self.update(msg)
102 def can_reduce(self):
103 return (self._mac is not None)
105 def get_len(self):
106 return self._total_len
108 def zero_pad(self):
109 if self._buffer_len & (self._bs-1):
110 npad = self._bs - self._buffer_len & (self._bs-1)
111 self._buffer.append(bchr(0)*npad)
112 self._buffer_len += npad
114 def update(self, data):
115 # Optimization (try not to copy data if possible)
116 if self._buffer_len==0 and self.can_reduce() and\
117 self._min_digest==0 and len(data)%self._bs==0:
118 self._update(data)
119 self._total_len += len(data)
120 return
122 self._buffer.append(data)
123 self._buffer_len += len(data)
124 self._total_len += len(data)
126 # Feed data into MAC
127 blocks, rem = divmod(self._buffer_len, self._bs)
128 if rem<self._min_digest:
129 blocks -= 1
130 if blocks>0 and self.can_reduce():
131 aligned_data = blocks*self._bs
132 buf = b("").join(self._buffer)
133 self._update(buf[:aligned_data])
134 self._buffer = [ buf[aligned_data:] ]
135 self._buffer_len -= aligned_data
137 def _deep_copy(self, target):
138 # Copy everything by self._mac, since we don't know how to
139 target._buffer = self._buffer[:]
140 for m in [ '_bs', '_buffer_len', '_total_len', '_min_digest', '_tag' ]:
141 setattr(target, m, getattr(self, m))
143 def _update(self, data_block):
144 """Delegate to the implementation the update
145 of the MAC state given some new *block aligned* data."""
146 raise NotImplementedError("_update() must be still implemented")
148 def _digest(self, left_data):
149 """Delegate to the implementation the computation
150 of the final MAC given the current MAC state
151 and the last piece of data (not block aligned)."""
152 raise NotImplementedError("_digest() must be still implemented")
154 def digest(self):
155 if self._tag:
156 return self._tag
157 if self._buffer_len>0:
158 self.update(b(""))
159 left_data = b("").join(self._buffer)
160 self._tag = self._digest(left_data)
161 return self._tag
163class CMAC(_SmoothMAC):
164 """Class that implements CMAC"""
166 #: The size of the authentication tag produced by the MAC.
167 digest_size = None
169 def __init__(self, key, msg = None, ciphermod = None):
170 """Create a new CMAC object.
172 :Parameters:
173 key : byte string
174 secret key for the CMAC object.
175 The key must be valid for the underlying cipher algorithm.
176 For instance, it must be 16 bytes long for AES-128.
177 msg : byte string
178 The very first chunk of the message to authenticate.
179 It is equivalent to an early call to `update`. Optional.
180 ciphermod : module
181 A cipher module from `Crypto.Cipher`.
182 The cipher's block size must be 64 or 128 bits.
183 It is recommended to use `Crypto.Cipher.AES`.
184 """
186 if ciphermod is None:
187 raise TypeError("ciphermod must be specified (try AES)")
189 _SmoothMAC.__init__(self, ciphermod.block_size, msg, 1)
191 self._key = key
192 self._factory = ciphermod
194 # Section 5.3 of NIST SP 800 38B
195 if ciphermod.block_size==8:
196 const_Rb = 0x1B
197 elif ciphermod.block_size==16:
198 const_Rb = 0x87
199 else:
200 raise TypeError("CMAC requires a cipher with a block size of 8 or 16 bytes, not %d" %
201 (ciphermod.block_size,))
202 self.digest_size = ciphermod.block_size
204 # Compute sub-keys
205 cipher = ciphermod.new(key, ciphermod.MODE_ECB)
206 l = cipher.encrypt(bchr(0)*ciphermod.block_size)
207 if bord(l[0]) & 0x80:
208 self._k1 = _shift_bytes(l, const_Rb)
209 else:
210 self._k1 = _shift_bytes(l)
211 if bord(self._k1[0]) & 0x80:
212 self._k2 = _shift_bytes(self._k1, const_Rb)
213 else:
214 self._k2 = _shift_bytes(self._k1)
216 # Initialize CBC cipher with zero IV
217 self._IV = bchr(0)*ciphermod.block_size
218 self._mac = ciphermod.new(key, ciphermod.MODE_CBC, self._IV)
220 def update(self, msg):
221 """Continue authentication of a message by consuming the next chunk of data.
223 Repeated calls are equivalent to a single call with the concatenation
224 of all the arguments. In other words:
226 >>> m.update(a); m.update(b)
228 is equivalent to:
230 >>> m.update(a+b)
232 :Parameters:
233 msg : byte string
234 The next chunk of the message being authenticated
235 """
237 _SmoothMAC.update(self, msg)
239 def _update(self, data_block):
240 self._IV = self._mac.encrypt(data_block)[-self._mac.block_size:]
242 def copy(self):
243 """Return a copy ("clone") of the MAC object.
245 The copy will have the same internal state as the original MAC
246 object.
247 This can be used to efficiently compute the MAC of strings that
248 share a common initial substring.
250 :Returns: A `CMAC` object
251 """
252 obj = CMAC(self._key, ciphermod=self._factory)
254 _SmoothMAC._deep_copy(self, obj)
255 obj._mac = self._factory.new(self._key, self._factory.MODE_CBC, self._IV)
256 for m in [ '_tag', '_k1', '_k2', '_IV']:
257 setattr(obj, m, getattr(self, m))
258 return obj
260 def digest(self):
261 """Return the **binary** (non-printable) MAC of the message that has
262 been authenticated so far.
264 This method does not change the state of the MAC object.
265 You can continue updating the object after calling this function.
267 :Return: A byte string of `digest_size` bytes. It may contain non-ASCII
268 characters, including null bytes.
269 """
270 return _SmoothMAC.digest(self)
272 def _digest(self, last_data):
273 if len(last_data)==self._bs:
274 last_block = strxor(last_data, self._k1)
275 else:
276 last_block = strxor(last_data+bchr(128)+
277 bchr(0)*(self._bs-1-len(last_data)), self._k2)
278 tag = self._mac.encrypt(last_block)
279 return tag
281 def hexdigest(self):
282 """Return the **printable** MAC of the message that has been
283 authenticated so far.
285 This method does not change the state of the MAC object.
287 :Return: A string of 2* `digest_size` bytes. It contains only
288 hexadecimal ASCII digits.
289 """
290 return "".join(["%02x" % bord(x)
291 for x in tuple(self.digest())])
293 def verify(self, mac_tag):
294 """Verify that a given **binary** MAC (computed by another party) is valid.
296 :Parameters:
297 mac_tag : byte string
298 The expected MAC of the message.
299 :Raises ValueError:
300 if the MAC does not match. It means that the message
301 has been tampered with or that the MAC key is incorrect.
302 """
304 mac = self.digest()
305 res = 0
306 # Constant-time comparison
307 for x,y in zip(mac, mac_tag):
308 res |= bord(x) ^ bord(y)
309 if res or len(mac_tag)!=self.digest_size:
310 raise ValueError("MAC check failed")
312 def hexverify(self, hex_mac_tag):
313 """Verify that a given **printable** MAC (computed by another party) is valid.
315 :Parameters:
316 hex_mac_tag : string
317 The expected MAC of the message, as a hexadecimal string.
318 :Raises ValueError:
319 if the MAC does not match. It means that the message
320 has been tampered with or that the MAC key is incorrect.
321 """
323 self.verify(unhexlify(tobytes(hex_mac_tag)))
325def new(key, msg = None, ciphermod = None):
326 """Create a new CMAC object.
328 :Parameters:
329 key : byte string
330 secret key for the CMAC object.
331 The key must be valid for the underlying cipher algorithm.
332 For instance, it must be 16 bytes long for AES-128.
333 msg : byte string
334 The very first chunk of the message to authenticate.
335 It is equivalent to an early call to `CMAC.update`. Optional.
336 ciphermod : module
337 A cipher module from `Crypto.Cipher`.
338 The cipher's block size must be 64 or 128 bits.
339 Default is `Crypto.Cipher.AES`.
341 :Returns: A `CMAC` object
342 """
343 return CMAC(key, msg, ciphermod)