Skip to content

Commit

Permalink
Merge python#13
Browse files Browse the repository at this point in the history
13: warn for hmac and hexlify r=ltratt a=nanjekyejoannah

This PR adds warnings for the following modules:
- `hmac`
- `binascii.hexlify`
- `binascii.b2a_hex`

See the notes below:

1. warn for the 'digest' parameter in hmac in pygrate2

In Python 2:

```
>>> import hashlib
>>> import hmac
>>> string1 = 'firststring'.encode('utf-8')
>>> string2 = 'secondstring'.encode('utf-8')
>>> digest = hmac.new(key=string1, msg=string2, digestmod=hashlib.sha256).digest()
>>> digest = hmac.new(key=string1, msg=string2).digest() 
>>> 
```

In Python 3:

```
string1 = 'firststring'.encode('utf-8')
>>> string2 = 'secondstring'.encode('utf-8')
>>> digest = hmac.new(key=string1, msg=string2, digestmod=hashlib.sha256).digest()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'hmac' is not defined
>>> import hashlib
>>> import hmac
>>> digest = hmac.new(key=string1, msg=string2, digestmod=hashlib.sha256).digest()
>>> digest = hmac.new(key=string1, msg=string2).digest() 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python@3.9/3.9.12_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/hmac.py", line 170, in new
    return HMAC(key, msg, digestmod)
  File "/usr/local/Cellar/python@3.9/3.9.12_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/hmac.py", line 56, in __init__
    raise TypeError("Missing required parameter 'digestmod'.")
TypeError: Missing required parameter 'digestmod'.
>>> 
```


2. Warn for hexlify 

In python 2:

```
>>> import codecs
>>> hexlify = codecs.getencoder('hex')
>>> hexlify(b'Blaah')[0]
'426c616168'
>>> import binascii
>>> binascii.hexlify(b'Blaah')
'426c616168'
>>> binascii.hexlify('Blaah')
'426c616168'
>>> 
```

In Python 3:

```
>>> import codecs
>>> hexlify = codecs.getencoder('hex')
>>> hexlify(b'Blaah')[0]
b'426c616168'
>>> import binascii
>>> binascii.hexlify(b'Blaah')
b'426c616168'
>>> binascii.hexlify('Blaah')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: a bytes-like object is required, not 'str'
>>> 
```


Co-authored-by: Joannah Nanjekye <jnanjekye@python.org>
  • Loading branch information
bors[bot] and nanjekyejoannah authored Nov 25, 2022
2 parents 48f8eb8 + c83c854 commit fa27e80
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 16 deletions.
6 changes: 6 additions & 0 deletions Lib/hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"""

import warnings as _warnings
import sys


from operator import _compare_digest as compare_digest

Expand Down Expand Up @@ -41,6 +43,10 @@ def __init__(self, key, msg = None, digestmod = None):
return

if digestmod is None:
if sys.py3kwarning:
_warnings.warnpy3k_with_fix('the digestmod paramemer is required in 3.x',
'generate a digest with hashlib module',
DeprecationWarning, stacklevel=4)
import hashlib
digestmod = hashlib.md5

Expand Down
7 changes: 5 additions & 2 deletions Lib/test/test_binascii.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,11 @@ def test_hex(self):
self.assertRaises(TypeError, binascii.a2b_hex, t[:-1] + 'q')

# Verify the treatment of Unicode strings
if test_support.have_unicode:
self.assertEqual(binascii.hexlify(unicode('a', 'ascii')), '61')
with test_support.check_py3k_warnings(("The hexlify() module expects bytes in 3.x; use the 'b' prefix on the string",
DeprecationWarning)):
if test_support.have_unicode:
self.assertEqual(binascii.hexlify(unicode('a', 'ascii')), '61')
self.assertEqual(binascii.b2a_hex(unicode('a', 'ascii')), '61')

def test_qp(self):
type2test = self.type2test
Expand Down
33 changes: 19 additions & 14 deletions Lib/test/test_hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,37 @@ def test_md5_vectors(self):
# Test the HMAC module against test vectors from the RFC.

def md5test(key, data, digest):
h = hmac.HMAC(key, data)
h = hmac.HMAC(key, data, digestmod=hashlib.sha1)
self.assertEqual(h.hexdigest().upper(), digest.upper())

md5test(chr(0x0b) * 16,
"Hi There",
"9294727A3638BB1C13F48EF8158BFC9D")
"675B0B3A1B4DDF4E124872DA6C2F632BFED957E9")

md5test("Jefe",
"what do ya want for nothing?",
"750c783e6ab0b503eaa86e310a5db738")
"EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79")

md5test(chr(0xAA)*16,
chr(0xDD)*50,
"56be34521d144c88dbb8c733f0e8b3f6")
"D730594D167E35D5956FD8003D0DB3D3F46DC7BB")

md5test("".join([chr(i) for i in range(1, 26)]),
chr(0xCD) * 50,
"697eaf0aca3a3aea3a75164746ffaa79")
"4C9007F4026250C6BC8414F9BF50C86C2D7235DA")

md5test(chr(0x0C) * 16,
"Test With Truncation",
"56461ef2342edc00f9bab995690efd4c")
"37268B7E21E84DA5720C53C4BA03AD1104039FA7")

md5test(chr(0xAA) * 80,
"Test Using Larger Than Block-Size Key - Hash Key First",
"6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd")
"AA4AE5E15272D00E95705637CE8A3B55ED402112")

md5test(chr(0xAA) * 80,
("Test Using Larger Than Block-Size Key "
"and Larger Than One Block-Size Data"),
"6f630fad67cda0ee1fb1f562db3aa53e")
"E8E99D0F45237D786D6BBAA7965C7808BBFF1A91")

def test_sha_vectors(self):
def shatest(key, data, digest):
Expand Down Expand Up @@ -232,14 +232,14 @@ def test_normal(self):
# Standard constructor call.
failed = 0
try:
h = hmac.HMAC("key")
h = hmac.HMAC("key", digestmod=hashlib.sha1)
except:
self.fail("Standard constructor call raised exception.")

def test_withtext(self):
# Constructor call with text.
try:
h = hmac.HMAC("key", "hash this!")
h = hmac.HMAC("key", "hash this!", digestmod=hashlib.sha1)
except:
self.fail("Constructor call with text argument raised exception.")

Expand All @@ -250,6 +250,11 @@ def test_withmodule(self):
except:
self.fail("Constructor call with hashlib.sha1 raised exception.")

def test_3k_no_digest(self):
with test_support.check_py3k_warnings(("the digestmod paramemer is required in 3.x; generate a digest with hashlib module",
DeprecationWarning)):
h = hmac.HMAC("key", "", hashlib.sha1)

class SanityTestCase(unittest.TestCase):

def test_default_is_md5(self):
Expand All @@ -262,7 +267,7 @@ def test_exercise_all_methods(self):
# Exercising all methods once.
# This must not raise any exceptions
try:
h = hmac.HMAC("my secret key")
h = hmac.HMAC("my secret key", digestmod=hashlib.sha1)
h.update("compute the hash of this text!")
dig = h.digest()
dig = h.hexdigest()
Expand All @@ -274,7 +279,7 @@ class CopyTestCase(unittest.TestCase):

def test_attributes(self):
# Testing if attributes are of same type.
h1 = hmac.HMAC("key")
h1 = hmac.HMAC("key", digestmod=hashlib.sha1)
h2 = h1.copy()
self.assertTrue(h1.digest_cons == h2.digest_cons,
"digest constructors don't match.")
Expand All @@ -285,7 +290,7 @@ def test_attributes(self):

def test_realcopy(self):
# Testing if the copy method created a real copy.
h1 = hmac.HMAC("key")
h1 = hmac.HMAC("key", digestmod=hashlib.sha1)
h2 = h1.copy()
# Using id() in case somebody has overridden __cmp__.
self.assertTrue(id(h1) != id(h2), "No real copy of the HMAC instance.")
Expand All @@ -296,7 +301,7 @@ def test_realcopy(self):

def test_equality(self):
# Testing if the copy has the same digests.
h1 = hmac.HMAC("key")
h1 = hmac.HMAC("key", digestmod=hashlib.sha1)
h1.update("some random text")
h2 = h1.copy()
self.assertTrue(h1.digest() == h2.digest(),
Expand Down
50 changes: 50 additions & 0 deletions Lib/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ def warnpy3k(message, category=None, stacklevel=1):
category = DeprecationWarning
warn(message, category, stacklevel+1)

def warnpy3k_with_fix(message, category=None, stacklevel=1):
"""Issue a deprecation warning for Python 3.x related changes and a fix.
Warnings are omitted unless Python is started with the -3 option.
"""
if sys.py3kwarning:
if category is None:
category = DeprecationWarning
warn_with_fix(message, fix, category, stacklevel+1)

def _show_warning(message, category, filename, lineno, file=None, line=None):
"""Hook to write a warning to a file; replace if you like."""
if file is None:
Expand Down Expand Up @@ -343,6 +353,46 @@ def warn_explicit(message, category, filename, lineno,
# Print message and context
showwarning(message, category, filename, lineno)

def warn_with_fix(message, fix, category=None, stacklevel=1):
"""Issue a warning, or maybe ignore it or raise an exception."""
# Check if message is already a Warning object
if isinstance(message, Warning):
category = message.__class__
# Check category argument
if category is None:
category = UserWarning
assert issubclass(category, Warning)
# Get context information
try:
caller = sys._getframe(stacklevel)
except ValueError:
globals = sys.__dict__
lineno = 1
else:
globals = caller.f_globals
lineno = caller.f_lineno
if '__name__' in globals:
module = globals['__name__']
else:
module = "<string>"
filename = globals.get('__file__')
if filename:
fnl = filename.lower()
if fnl.endswith((".pyc", ".pyo")):
filename = filename[:-1]
else:
if module == "__main__":
try:
filename = sys.argv[0]
except AttributeError:
# embedded interpreters don't have sys.argv, see bug #839151
filename = '__main__'
if not filename:
filename = module
registry = globals.setdefault("__warningregistry__", {})
warn_explicit_with_fix(message, fix, category, filename, lineno, module, registry,
globals)

def warn_explicit_with_fix(message, fix, category, filename, lineno,
module=None, registry=None, module_globals=None):
lineno = int(lineno)
Expand Down
5 changes: 5 additions & 0 deletions Modules/binascii.c
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,11 @@ binascii_hexlify(PyObject *self, PyObject *args)
argbuf = parg.buf;
arglen = parg.len;

if (PyUnicode_Check(args))
if (PyErr_WarnPy3k_WithFix("The hexlify() module expects bytes in 3.x",
"use the 'b' prefix on the string", 1) < 0)
return NULL;

assert(arglen >= 0);
if (arglen > PY_SSIZE_T_MAX / 2) {
PyBuffer_Release(&parg);
Expand Down

0 comments on commit fa27e80

Please sign in to comment.