0
# Message Authentication
1
2
Message authentication codes (MACs) and digital signatures for ensuring data integrity and authenticity. Protects against tampering and verifies message origin.
3
4
## Core Imports
5
6
```python
7
from cryptography.hazmat.primitives import hmac, cmac, poly1305
8
from cryptography.hazmat.primitives import hashes
9
from cryptography.hazmat.primitives.ciphers import algorithms
10
```
11
12
## Capabilities
13
14
### HMAC (Hash-based Message Authentication Code)
15
16
```python { .api }
17
class HMAC:
18
def __init__(self, key: bytes, algorithm, backend=None):
19
"""
20
Initialize HMAC with key and hash algorithm.
21
22
Args:
23
key (bytes): Secret key for authentication
24
algorithm: Hash algorithm (hashes.SHA256(), etc.)
25
backend: Cryptographic backend
26
"""
27
28
def update(self, data: bytes) -> None:
29
"""
30
Add data to MAC computation.
31
32
Args:
33
data (bytes): Data to authenticate
34
"""
35
36
def finalize(self) -> bytes:
37
"""
38
Finalize MAC and return authentication tag.
39
40
Returns:
41
bytes: HMAC authentication tag
42
"""
43
44
def verify(self, signature: bytes) -> None:
45
"""
46
Verify HMAC signature.
47
48
Args:
49
signature (bytes): Expected HMAC tag
50
51
Raises:
52
InvalidSignature: If verification fails
53
"""
54
55
def copy(self) -> 'HMAC':
56
"""Create copy of current HMAC state"""
57
```
58
59
### CMAC (Cipher-based Message Authentication Code)
60
61
```python { .api }
62
class CMAC:
63
def __init__(self, key: bytes, algorithm, backend=None):
64
"""
65
Initialize CMAC with key and cipher algorithm.
66
67
Args:
68
key (bytes): Secret key for authentication
69
algorithm: Block cipher algorithm (algorithms.AES(), etc.)
70
backend: Cryptographic backend
71
"""
72
73
def update(self, data: bytes) -> None:
74
"""Add data to CMAC computation"""
75
76
def finalize(self) -> bytes:
77
"""Finalize and return CMAC tag"""
78
79
def verify(self, signature: bytes) -> None:
80
"""
81
Verify CMAC signature.
82
83
Raises:
84
InvalidSignature: If verification fails
85
"""
86
87
def copy(self) -> 'CMAC':
88
"""Create copy of CMAC state"""
89
```
90
91
### Poly1305
92
93
```python { .api }
94
class Poly1305:
95
def __init__(self, key: bytes):
96
"""
97
Initialize Poly1305 MAC.
98
99
Args:
100
key (bytes): 32-byte secret key
101
"""
102
103
def update(self, data: bytes) -> None:
104
"""Add data to Poly1305 computation"""
105
106
def finalize(self) -> bytes:
107
"""
108
Finalize Poly1305 MAC.
109
110
Returns:
111
bytes: 16-byte authentication tag
112
"""
113
114
def verify(self, tag: bytes) -> None:
115
"""
116
Verify Poly1305 tag.
117
118
Raises:
119
InvalidSignature: If verification fails
120
"""
121
122
@classmethod
123
def generate_key(cls) -> bytes:
124
"""Generate random 32-byte key for Poly1305"""
125
```
126
127
## Usage Examples
128
129
### HMAC with SHA-256
130
131
```python
132
from cryptography.hazmat.primitives import hmac, hashes
133
from cryptography.exceptions import InvalidSignature
134
import os
135
136
# Generate HMAC key
137
key = os.urandom(32) # 256-bit key
138
139
# Create message to authenticate
140
message = b"Important message that needs authentication"
141
142
# Compute HMAC
143
h = hmac.HMAC(key, hashes.SHA256())
144
h.update(message)
145
tag = h.finalize()
146
147
print(f"HMAC-SHA256: {tag.hex()}")
148
149
# Verify HMAC
150
h_verify = hmac.HMAC(key, hashes.SHA256())
151
h_verify.update(message)
152
153
try:
154
h_verify.verify(tag)
155
print("HMAC verification: SUCCESS")
156
except InvalidSignature:
157
print("HMAC verification: FAILED")
158
```
159
160
### CMAC with AES
161
162
```python
163
from cryptography.hazmat.primitives import cmac
164
from cryptography.hazmat.primitives.ciphers import algorithms
165
from cryptography.exceptions import InvalidSignature
166
import os
167
168
# Generate AES key for CMAC
169
key = os.urandom(32) # 256-bit AES key
170
171
message = b"Data to authenticate with CMAC-AES"
172
173
# Compute CMAC
174
c = cmac.CMAC(key, algorithms.AES)
175
c.update(message)
176
tag = c.finalize()
177
178
print(f"CMAC-AES: {tag.hex()}")
179
180
# Verify CMAC
181
c_verify = cmac.CMAC(key, algorithms.AES)
182
c_verify.update(message)
183
184
try:
185
c_verify.verify(tag)
186
print("CMAC verification: SUCCESS")
187
except InvalidSignature:
188
print("CMAC verification: FAILED")
189
```
190
191
### Poly1305 Authentication
192
193
```python
194
from cryptography.hazmat.primitives import poly1305
195
from cryptography.exceptions import InvalidSignature
196
197
# Generate Poly1305 key
198
key = poly1305.Poly1305.generate_key()
199
200
message = b"Message authenticated with Poly1305"
201
202
# Compute Poly1305 tag
203
p = poly1305.Poly1305(key)
204
p.update(message)
205
tag = p.finalize()
206
207
print(f"Poly1305 tag: {tag.hex()}")
208
209
# Verify tag
210
p_verify = poly1305.Poly1305(key)
211
p_verify.update(message)
212
213
try:
214
p_verify.verify(tag)
215
print("Poly1305 verification: SUCCESS")
216
except InvalidSignature:
217
print("Poly1305 verification: FAILED")
218
```
219
220
### Message Authentication for API Requests
221
222
```python
223
from cryptography.hazmat.primitives import hmac, hashes
224
import time
225
import json
226
import hashlib
227
228
class APIAuthenticator:
229
def __init__(self, secret_key: bytes):
230
self.secret_key = secret_key
231
232
def sign_request(self, method: str, url: str, body: bytes = None, timestamp: int = None) -> str:
233
"""Sign API request with HMAC"""
234
if timestamp is None:
235
timestamp = int(time.time())
236
237
# Create string to sign
238
body_hash = hashlib.sha256(body or b"").hexdigest()
239
string_to_sign = f"{method}\n{url}\n{body_hash}\n{timestamp}"
240
241
# Compute HMAC
242
h = hmac.HMAC(self.secret_key, hashes.SHA256())
243
h.update(string_to_sign.encode())
244
signature = h.finalize()
245
246
return {
247
'signature': signature.hex(),
248
'timestamp': timestamp
249
}
250
251
def verify_request(self, method: str, url: str, body: bytes, signature: str, timestamp: int) -> bool:
252
"""Verify API request signature"""
253
# Check timestamp (prevent replay attacks)
254
current_time = int(time.time())
255
if abs(current_time - timestamp) > 300: # 5 minute window
256
return False
257
258
# Verify signature
259
expected_sig = self.sign_request(method, url, body, timestamp)
260
return expected_sig['signature'] == signature
261
262
# Usage
263
api_key = os.urandom(32)
264
authenticator = APIAuthenticator(api_key)
265
266
# Sign request
267
request_body = json.dumps({"user": "alice", "action": "transfer"}).encode()
268
auth_data = authenticator.sign_request("POST", "/api/transfer", request_body)
269
270
print(f"Request signature: {auth_data['signature']}")
271
print(f"Timestamp: {auth_data['timestamp']}")
272
273
# Verify request
274
is_valid = authenticator.verify_request(
275
"POST", "/api/transfer", request_body,
276
auth_data['signature'], auth_data['timestamp']
277
)
278
print(f"Request valid: {is_valid}")
279
```
280
281
### File Integrity with HMAC
282
283
```python
284
from cryptography.hazmat.primitives import hmac, hashes
285
import os
286
287
class FileIntegrityChecker:
288
def __init__(self, key: bytes):
289
self.key = key
290
291
def compute_file_hmac(self, filepath: str) -> bytes:
292
"""Compute HMAC for file contents"""
293
h = hmac.HMAC(self.key, hashes.SHA256())
294
295
with open(filepath, 'rb') as f:
296
while chunk := f.read(8192):
297
h.update(chunk)
298
299
return h.finalize()
300
301
def create_integrity_file(self, filepath: str):
302
"""Create .hmac file with integrity hash"""
303
file_hmac = self.compute_file_hmac(filepath)
304
305
with open(f"{filepath}.hmac", 'wb') as f:
306
f.write(file_hmac)
307
308
print(f"Integrity file created: {filepath}.hmac")
309
310
def verify_file_integrity(self, filepath: str) -> bool:
311
"""Verify file against .hmac file"""
312
try:
313
with open(f"{filepath}.hmac", 'rb') as f:
314
stored_hmac = f.read()
315
316
current_hmac = self.compute_file_hmac(filepath)
317
return stored_hmac == current_hmac
318
319
except FileNotFoundError:
320
print(f"Integrity file not found: {filepath}.hmac")
321
return False
322
323
# Usage
324
integrity_key = os.urandom(32)
325
checker = FileIntegrityChecker(integrity_key)
326
327
# Create test file
328
with open('important_document.txt', 'w') as f:
329
f.write("This is an important document that must not be tampered with.\n")
330
331
# Create integrity hash
332
checker.create_integrity_file('important_document.txt')
333
334
# Verify integrity
335
is_intact = checker.verify_file_integrity('important_document.txt')
336
print(f"File integrity: {'OK' if is_intact else 'COMPROMISED'}")
337
338
# Simulate tampering
339
with open('important_document.txt', 'a') as f:
340
f.write("Unauthorized modification\n")
341
342
# Check integrity again
343
is_intact = checker.verify_file_integrity('important_document.txt')
344
print(f"File integrity after modification: {'OK' if is_intact else 'COMPROMISED'}")
345
```
346
347
### Streaming Authentication
348
349
```python
350
from cryptography.hazmat.primitives import hmac, hashes
351
from cryptography.exceptions import InvalidSignature
352
353
class StreamingMAC:
354
def __init__(self, key: bytes):
355
self.key = key
356
self.hmac = hmac.HMAC(key, hashes.SHA256())
357
358
def update(self, data: bytes):
359
"""Add data to streaming MAC"""
360
self.hmac.update(data)
361
362
def finalize(self) -> bytes:
363
"""Get final MAC tag"""
364
return self.hmac.finalize()
365
366
def verify_stream(self, expected_tag: bytes) -> bool:
367
"""Verify streaming MAC"""
368
# Create new HMAC for verification (original is finalized)
369
verify_hmac = hmac.HMAC(self.key, hashes.SHA256())
370
371
# Re-process all data would require storing it
372
# In practice, you'd compute MAC during initial processing
373
try:
374
verify_hmac.verify(expected_tag)
375
return True
376
except InvalidSignature:
377
return False
378
379
# Usage for streaming data
380
key = os.urandom(32)
381
stream_mac = StreamingMAC(key)
382
383
# Process data in chunks
384
data_chunks = [b"chunk1", b"chunk2", b"chunk3", b"final_chunk"]
385
386
for chunk in data_chunks:
387
stream_mac.update(chunk)
388
print(f"Processed chunk: {chunk}")
389
390
# Get final authentication tag
391
final_tag = stream_mac.finalize()
392
print(f"Stream MAC: {final_tag.hex()}")
393
```
394
395
## Security Considerations
396
397
- **Key Management**: Use different keys for different purposes
398
- **Key Length**: Use appropriately sized keys (32+ bytes for HMAC)
399
- **Timing Attacks**: Use constant-time comparison for MAC verification
400
- **Replay Protection**: Include timestamps or nonces in authenticated data
401
- **Algorithm Selection**: HMAC-SHA256 for most applications, Poly1305 for performance
402
- **MAC-then-Encrypt**: Apply MAC to plaintext, then encrypt both message and MAC
403
- **Verification**: Always verify MAC before processing authenticated data