Skip to content

Commit

Permalink
Merge pull request #55506 from Ajnbro/hmac-compute
Browse files Browse the repository at this point in the history
Add hmac_compute (Repost of #54164)
  • Loading branch information
dwoz committed Dec 13, 2019
2 parents 6454562 + 463b639 commit 73b8ce0
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 18 deletions.
27 changes: 21 additions & 6 deletions salt/modules/hashutil.py
Expand Up @@ -115,13 +115,13 @@ def base64_b64decode(instr):

def base64_encodestring(instr):
'''
Encode a string as base64 using the "legacy" Python interface.
Encode a byte-like object as base64 using the "modern" Python interface.
Among other possible differences, the "legacy" encoder includes
Among other possible differences, the "modern" encoder includes
a newline ('\\n') character after every 76 characters and always
at the end of the encoded string.
at the end of the encoded byte-like object.
.. versionadded:: 2014.7.0
.. versionadded:: Neon
CLI Example:
Expand Down Expand Up @@ -167,9 +167,9 @@ def base64_encodefile(fname):

def base64_decodestring(instr):
'''
Decode a base64-encoded string using the "legacy" Python interface
Decode a base64-encoded byte-like object using the "modern" Python interface
.. versionadded:: 2014.7.0
.. versionadded:: Neon
CLI Example:
Expand Down Expand Up @@ -263,6 +263,21 @@ def hmac_signature(string, shared_secret, challenge_hmac):
return salt.utils.hashutils.hmac_signature(string, shared_secret, challenge_hmac)


def hmac_compute(string, shared_secret):
'''
.. versionadded:: Sodium
Compute a HMAC SHA256 digest using a string and secret.
CLI Example:
.. code-block:: bash
salt '*' hashutil.hmac_compute 'get salted' 'shared secret'
'''
return salt.utils.hashutils.hmac_compute(string, shared_secret)


def github_signature(string, shared_secret, challenge_hmac):
'''
Verify a challenging hmac signature against a string / shared-secret for
Expand Down
45 changes: 33 additions & 12 deletions salt/utils/hashutils.py
Expand Up @@ -51,29 +51,39 @@ def base64_b64decode(instr):

def base64_encodestring(instr):
'''
Encode a string as base64 using the "legacy" Python interface.
Encode a byte-like object as base64 using the "modern" Python interface.
Among other possible differences, the "legacy" encoder includes
Among other possible differences, the "modern" encoder includes
a newline ('\\n') character after every 76 characters and always
at the end of the encoded string.
'''
# Handles PY2
if six.PY2:
return salt.utils.stringutils.to_unicode(
base64.encodestring(salt.utils.stringutils.to_bytes(instr)),
encoding='utf8' if salt.utils.platform.is_windows() else None
)

# Handles PY3
return salt.utils.stringutils.to_unicode(
base64.encodestring(salt.utils.stringutils.to_bytes(instr)),
base64.encodebytes(salt.utils.stringutils.to_bytes(instr)),
encoding='utf8' if salt.utils.platform.is_windows() else None
)


def base64_decodestring(instr):
'''
Decode a base64-encoded string using the "legacy" Python interface.
Decode a base64-encoded byte-like object using the "modern" Python interface.
'''
b = salt.utils.stringutils.to_bytes(instr)
try:
# PY3
decoded = base64.decodebytes(b)
except AttributeError:
# PY2
decoded = base64.decodestring(b)
bvalue = salt.utils.stringutils.to_bytes(instr)

if six.PY3:
# Handle PY3
decoded = base64.decodebytes(bvalue)
else:
# Handle PY2
decoded = base64.decodestring(bvalue)

try:
return salt.utils.stringutils.to_unicode(
decoded,
Expand All @@ -93,6 +103,7 @@ def md5_digest(instr):
)


@jinja_filter('sha1')
def sha1_digest(instr):
'''
Generate an sha1 hash of a given string.
Expand Down Expand Up @@ -137,7 +148,17 @@ def hmac_signature(string, shared_secret, challenge_hmac):
return valid_hmac == challenge


@jinja_filter('rand_str') # Remove this for Neon
@jinja_filter('hmac_compute')
def hmac_compute(string, shared_secret):
'''
Create an hmac digest.
'''
msg = salt.utils.stringutils.to_bytes(string)
key = salt.utils.stringutils.to_bytes(shared_secret)
hmac_hash = hmac.new(key, msg, hashlib.sha256).hexdigest()
return hmac_hash


@jinja_filter('random_hash')
def random_hash(size=9999999999, hash_type=None):
'''
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/modules/test_hashutil.py
Expand Up @@ -20,6 +20,7 @@ class HashutilTestCase(ModuleCase):
the_string_sha256 = 'd49859ccbc854fa68d800b5734efc70d72383e6479d545468bc300263164ff33'
the_string_sha512 = 'a8c174a7941c64a068e686812a2fafd7624c840fde800f5965fbeca675f2f6e37061ffe41e17728c919bdea290eab7a21e13c04ae71661955a87f2e0e04bb045'
the_string_hmac = 'eBWf9bstXg+NiP5AOwppB5HMvZiYMPzEM9W5YMm/AmQ='
the_string_hmac_compute = '78159ff5bb2d5e0f8d88fe403b0a690791ccbd989830fcc433d5b960c9bf0264'
the_string_github = 'sha1=b06aa56bdf4935eec82c4e53e83ed03f03fdb32d'

def setUp(self):
Expand Down Expand Up @@ -53,6 +54,10 @@ def test_hmac_signature(self):
self.the_string_hmac)
self.assertTrue(ret)

def test_hmac_compute(self):
ret = self.hashutil['hashutil.hmac_compute'](self.the_string, 'shared secret')
self.assertEqual(ret, self.the_string_hmac_compute)

def test_github_signature(self):
ret = self.hashutil['hashutil.github_signature'](
self.the_string,
Expand Down
17 changes: 17 additions & 0 deletions tests/unit/utils/test_hashutils.py
Expand Up @@ -23,6 +23,7 @@ class HashutilsTestCase(TestCase):
str_sha256 = '095291ffa3d361436d4617879e22c1da06c6ab61a3fb081321ec854a27a091ac'
str_sha512 = '12efd90e507289f1f21e5dcfe2e92cf0bb4904abccb55c3ce9177670c711981501054b32b807c37058675590d1c484bd2b72a4215a2fa397aa4f2b12f298b1f0'
str_hmac_challenge = b'qz2k0t1aevKEme3JGsNQJX/xpmf+/w3q6qmWDk1ZqbY='
str_hmac_compute = 'ab3da4d2dd5a7af28499edc91ac350257ff1a667feff0deaeaa9960e4d59a9b6'

# 16 bytes of random data
bytes = b'b\x19\xf6\x86\x0e\x1a\x1cs\x0c\xda&zv\xfc\xa2\xdd'
Expand All @@ -32,6 +33,7 @@ class HashutilsTestCase(TestCase):
bytes_sha256 = '25711a31c2673a48f3d1f29b25add574697872968e546d266f441de63b17954a'
bytes_sha512 = '69f1524e602c1599fc374e1e3e2941e6f6949f4f7fe7321304e4e67bb850f3204dd5cbf9c13e231814540c2f5cd370c24ea257771d9fbf311d8f6085bad12b24'
bytes_hmac_challenge = b'lQibiD9r1Hpo+5JYknaudIKfTx1L5J3U58M9yQOd04c='
bytes_hmac_compute = '95089b883f6bd47a68fb92589276ae74829f4f1d4be49dd4e7c33dc9039dd387'

def test_base64_b64encode(self):
'''
Expand Down Expand Up @@ -157,6 +159,21 @@ def test_hmac_signature(self):
)
)

def test_hmac_compute(self):
'''
Ensure that this function converts the value passed to bytes before
attempting to encode, avoiding a UnicodeEncodeError on Python 2 and a
TypeError on Python 3.
'''
self.assertEqual(
salt.utils.hashutils.hmac_compute(self.str, self.hmac_secret),
self.str_hmac_compute
)
self.assertEqual(
salt.utils.hashutils.hmac_compute(self.bytes, self.hmac_secret),
self.bytes_hmac_compute
)

def test_get_hash_exception(self):
self.assertRaises(
ValueError,
Expand Down

0 comments on commit 73b8ce0

Please sign in to comment.