Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hmac_compute #54164

Closed
wants to merge 9 commits into from
27 changes: 21 additions & 6 deletions salt/modules/hashutil.py
Original file line number Diff line number Diff line change
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:: Sodium

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:: Sodium

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
28 changes: 17 additions & 11 deletions salt/utils/hashutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,29 +51,24 @@ 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.
'''
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)
decoded = base64.decodebytes(bvalue)
try:
return salt.utils.stringutils.to_unicode(
decoded,
Expand Down Expand Up @@ -138,6 +133,17 @@ def hmac_signature(string, shared_secret, challenge_hmac):
return valid_hmac == challenge


@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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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