Skip to content

Commit

Permalink
md: Pure-Python impl of hash and hmac
Browse files Browse the repository at this point in the history
  • Loading branch information
Synss committed May 5, 2019
1 parent 8a2e5e0 commit 9a77381
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 202 deletions.
2 changes: 1 addition & 1 deletion ChangeLog
@@ -1,6 +1,6 @@
[next]

* cipher: Make package pure-Python.
* cipher/hash/hmac: These modules and packages are now pure-Python.
* mpi: Add bitwise operations.

API Changes
Expand Down
11 changes: 9 additions & 2 deletions src/mbedtls/_md.pxd
Expand Up @@ -18,8 +18,7 @@ cdef extern from "mbedtls/md.h" nogil:
const mbedtls_md_info_t *md_info

const int *mbedtls_md_list()
const mbedtls_md_info_t *mbedtls_md_info_from_string(
const char *md_name)
const mbedtls_md_info_t *mbedtls_md_info_from_string(const char *md_name)
# mbedtls_md_info_from_type

void mbedtls_md_init(mbedtls_md_context_t *ctx)
Expand Down Expand Up @@ -66,3 +65,11 @@ cdef class MDBase:
cdef const mbedtls_md_info_t* _info
cdef mbedtls_md_context_t _ctx
cdef _finish(self, const unsigned char *output)


cdef class Hash(MDBase):
pass


cdef class Hmac(MDBase):
pass
108 changes: 102 additions & 6 deletions src/mbedtls/_md.pyx
Expand Up @@ -66,12 +66,11 @@ cdef class MDBase:
name (str): The name of the message digest.
"""
def __init__(self, name, buffer, hmac):
if not isinstance(name, str):
raise TypeError("name must be a string: got %r" % name)
self._info = _md.mbedtls_md_info_from_string(
name.upper().encode("ascii"))
check_error(_md.mbedtls_md_setup(&self._ctx, self._info, hmac))
def __init__(self, name, _hmac):
name_ = name.upper().encode("ascii", "strict")
cdef char *c_name = name_
self._info = _md.mbedtls_md_info_from_string(c_name)
check_error(_md.mbedtls_md_setup(&self._ctx, self._info, _hmac))

def __cinit__(self):
"""Initialize an `md_context` (as NONE)."""
Expand Down Expand Up @@ -134,3 +133,100 @@ cdef class MDBase:
def copy(self):
"""Return a copy ("clone") of the MD object."""
raise NotImplementedError


cdef class Hash(_md.MDBase):

"""Wrap and encapsulate hash calculations.
This class is a wrapper for the hash calculations in the md module
of mbed TLS. The interface follows the recommendation from PEP 452
for unkeyed hashes.
Parameters:
name (str): The MD name known to mbed TLS.
Attributes:
digest_size (int): The size of the message digest, in bytes.
block_size (int): The internal block size of the hash
algorithm in bytes.
name (str): The name of the message digest.
"""
def __init__(self, name, buffer=None):
super().__init__(name, 0)
check_error(_md.mbedtls_md_starts(&self._ctx))
self.update(buffer)

def update(self, const unsigned char[:] buffer):
"""Update the hash object with the `buffer`."""
if buffer is None or buffer.size == 0:
return
check_error(
_md.mbedtls_md_update(&self._ctx, &buffer[0], buffer.size))

cdef _finish(self, unsigned char *output):
"""Return the digest output of `message`."""
return _md.mbedtls_md_finish(&self._ctx, output)

def copy(self):
"""Return a copy ("clone") of the hash object."""
obj = Hash(self.name)
check_error(_md.mbedtls_md_clone(&obj._ctx, &self._ctx))
return obj


cdef class Hmac(_md.MDBase):

"""Wrap and encapsulate HMAC calculations.
This class is a wrapper for the HMAC calculations in the md module
of mbed TLS. The interface follows the recommendation from PEP 452
for keyed hashes.
Parameters:
key (bytes): The key to use.
name (bytes): The MD name known to mbed TLS.
Warning:
The message is cleared after calculation of the digest. Only
call :meth:`digest` or :meth:`hexdigest` once per message.
Attributes:
digest_size (int): The size of the message digest, in bytes.
block_size (int): The internal block size of the hash
algorithm in bytes.
name (bytes): The name of the message digest.
"""
def __init__(
self, const unsigned char[:] key not None, name, buffer=None
):
super().__init__(name, 1)
if not key.size:
key = b"\0"
check_error(_md.mbedtls_md_hmac_starts(&self._ctx, &key[0], key.size))
self.update(buffer)

def update(self, const unsigned char[:] buffer):
"""Update the HMAC object with `buffer`."""
if buffer is None or buffer.size == 0:
return
check_error(
_md.mbedtls_md_hmac_update(&self._ctx, &buffer[0], buffer.size))

cdef _finish(self, unsigned char *output):
"""Return the HMAC of key and message."""
ret = _md.mbedtls_md_hmac_finish(&self._ctx, output)
if ret != 0:
return ret
return _md.mbedtls_md_hmac_reset(&self._ctx)

def copy(self):
"""Return a copy ("clone") of the HMAC object.
Warning:
Not implemented in mbed TLS, raises NotImplementedError.
"""
raise NotImplementedError
12 changes: 0 additions & 12 deletions src/mbedtls/hash.pxd

This file was deleted.

47 changes: 5 additions & 42 deletions src/mbedtls/hash.pyx → src/mbedtls/hash.py
@@ -1,12 +1,13 @@
"""Generic message digest wrapper (hash algorithm)."""

__author__ = "Mathias Laurin"
__copyright__ = "Copyright 2015, Elaborated Networks GmbH"
__copyright__ = (
"Copyright 2015, Elaborated Networks GmbH, "
"Copyright 2019, Mathias Laurin"
)
__license__ = "MIT License"


from libc.stdlib cimport malloc, free
cimport mbedtls._md as _md
import mbedtls._md as _md
from mbedtls.exceptions import *

Expand All @@ -15,45 +16,7 @@
algorithms_available = _md.algorithms_available


cdef class Hash(_md.MDBase):

"""Wrap and encapsulate hash calculations.
This class is a wrapper for the hash calculations in the md module
of mbed TLS. The interface follows the recommendation from PEP 452
for unkeyed hashes.
Parameters:
name (str): The MD name known to mbed TLS.
Attributes:
digest_size (int): The size of the message digest, in bytes.
block_size (int): The internal block size of the hash
algorithm in bytes.
name (str): The name of the message digest.
"""
def __init__(self, name, buffer=None):
super().__init__(name, buffer, 0)
check_error(_md.mbedtls_md_starts(&self._ctx))
self.update(buffer)

def update(self, const unsigned char[:] buffer):
"""Update the hash object with the `buffer`."""
if buffer is None or buffer.size == 0:
return
check_error(
_md.mbedtls_md_update(&self._ctx, &buffer[0], buffer.size))

cdef _finish(self, unsigned char *output):
"""Return the digest output of `message`."""
return _md.mbedtls_md_finish(&self._ctx, output)

def copy(self):
"""Return a copy ("clone") of the hash object."""
obj = Hash(self.name)
check_error(_md.mbedtls_md_clone(&obj._ctx, &self._ctx))
return obj
Hash = _md.Hash


def new(name, buffer=None):
Expand Down
2 changes: 1 addition & 1 deletion src/mbedtls/hkdf.pyx
Expand Up @@ -11,7 +11,7 @@ __license__ = "MIT License"

from libc.stdlib cimport malloc, free

cimport mbedtls.hmac as _hmac
cimport mbedtls._md as _hmac
cimport mbedtls.hkdf as _hkdf

import mbedtls.hmac as _hmac
Expand Down
12 changes: 0 additions & 12 deletions src/mbedtls/hmac.pxd

This file was deleted.

75 changes: 75 additions & 0 deletions src/mbedtls/hmac.py
@@ -0,0 +1,75 @@
"""Generic message digest wrapper (hash algorithm)."""

__author__ = "Mathias Laurin"
__copyright__ = (
"Copyright 2015, Elaborated Networks GmbH, "
"Copyright 2019, Mathias Laurin"
)
__license__ = "MIT License"


import mbedtls._md as _md
from mbedtls.exceptions import *


algorithms_guaranteed = _md.algorithms_guaranteed
algorithms_available = _md.algorithms_available


Hmac = _md.Hmac


def new(key, buffer=None, digestmod=None):
"""A generic constructor that takes the key algorithm as its first
parameter.
"""
if digestmod is None:
digestmod = "md5"
return Hmac(key, digestmod, buffer)


def md2(key, buffer=None):
"""MD2 message-digest algorithm."""
return Hmac(key, "md2", buffer)


def md4(key, buffer=None):
"""MD4 message-digest algorithm."""
return Hmac(key, "md4", buffer)


def md5(key, buffer=None):
"""MD5 message-digest algorithm."""
return Hmac(key, "md5", buffer)


def sha1(key, buffer=None):
"""Secure Hmac Algorithm 1 (SHA-1)."""
return Hmac(key, "sha1", buffer)


def sha224(key, buffer=None):
"""Secure Hmac Algorithm 2 (SHA-2) with 224 bits hash value."""
return Hmac(key, "sha224", buffer)


def sha256(key, buffer=None):
"""Secure Hmac Algorithm 2 (SHA-2) with 256 bits hash value."""
return Hmac(key, "sha256", buffer)


def sha384(key, buffer=None):
"""Secure Hmac Algorithm 2 (SHA-2) with 384 bits hash value."""
return Hmac(key, "sha384", buffer)


def sha512(key, buffer=None):
"""Secure Hmac Algorithm 2 (SHA-2) with 512 bits hash value."""
return Hmac(key, "sha512", buffer)


def ripemd160(key, buffer=None):
"""RACE Integrity Primitives Evaluation Message Digest (RIPEMD) with
160 bits hash value."""
return Hmac(key, "ripemd160", buffer)

0 comments on commit 9a77381

Please sign in to comment.