<commit_before>/*
 * Copyright (c) 2016, Pycom Limited.
 *
 * This software is licensed under the GNU GPL version 3 or any
 * later version, with permitted additional terms. For more information
 * see the Pycom Licence v1.0 document supplied with this file, or
 * available at https://www.pycom.io/opensource/licensing
 */

#include <assert.h>
#include <string.h>

#include "py/mpconfig.h"
#include "py/nlr.h"
#include "py/runtime.h"
#include "rom/md5_hash.h"
#include "hwcrypto/sha.h"
#include "mpexception.h"

/******************************************************************************
 DEFINE PRIVATE TYPES
 ******************************************************************************/
typedef struct _mp_obj_hash_t {
    mp_obj_base_t base;
    uint8_t  b_size;
    uint8_t  h_size;
    uint8_t  buf_count;
    bool  digested;
    uint8_t buffer[128];
    union {
        struct MD5Context md5_context;
        esp_sha_context sha_context;
    };
} mp_obj_hash_t;

/******************************************************************************
 DECLARE PRIVATE FUNCTIONS
 ******************************************************************************/
STATIC void hash_update_internal(mp_obj_t self_in, mp_obj_t data, bool digest);
STATIC mp_obj_t hash_read (mp_obj_t self_in);

/******************************************************************************
 DEFINE PRIVATE FUNCTIONS
 ******************************************************************************/


STATIC void generic_hash_update(mp_obj_hash_t *self, void *data, uint32_t len) {
    switch (self->base.type->name) {
    case MP_QSTR_sha1:
        esp_sha1_update(&self->sha_context, data, len);
        break;

    case MP_QSTR_sha256:
        esp_sha256_update(&self->sha_context, data, len);
        break;

    case MP_QSTR_sha384:
    case MP_QSTR_sha512:
        esp_sha512_update(&self->sha_context, data, len);
        break;

    case MP_QSTR_md5:
        MD5Update(&self->md5_context, data, len);
        break;
    }
}

STATIC void hash_update_internal(mp_obj_t self_in, mp_obj_t data, bool digest) {
    mp_obj_hash_t *self = self_in;
    mp_buffer_info_t bufinfo;
    uint32_t len;

    if (digest == true) {
        generic_hash_update(self, self->buffer, self->buf_count);
        return;
    }

    if (!data) {
        return;
    }

    mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);

    // Try to fill the buffer to the top
    len = self->b_size - self->buf_count;
    len = MIN(len, bufinfo.len);

    memcpy(self->buffer + self->buf_count, bufinfo.buf, len);

    self->buf_count += len;

    // check if there is a complete block
    if (self->buf_count != self->b_size && digest == false) {
        return;
    }

    // process it
    generic_hash_update(self, self->buffer, self->buf_count);
    self->buf_count = 0;

    // update the input string information
    bufinfo.buf = ((uint8_t *) bufinfo.buf) + len;
    bufinfo.len -= len;

    // see if there is at least another complete block
    len = bufinfo.len / self->b_size;
    if (len != 0) {
        // process it (them)
        len *= self->b_size;
        generic_hash_update(self, bufinfo.buf, len);

        // update the input string information
        bufinfo.buf = ((uint8_t *) bufinfo.buf) + len;
        bufinfo.len -= len;
    }

    // and copy any remaining bytes to the buffer
    memcpy(self->buffer, bufinfo.buf, bufinfo.len);
    self->buf_count = bufinfo.len;
}

STATIC mp_obj_t hash_read(mp_obj_t self_in) {
    mp_obj_hash_t *self = self_in;

    if (!self->digested) {
        // process any remaining data in the buffer
        hash_update_internal(self, MP_OBJ_NULL, true);

        switch (self->base.type->name) {
        case MP_QSTR_sha1:
            esp_sha1_finish(&self->sha_context, (uint8_t *)self->buffer);
            esp_sha1_free(&self->sha_context);
            break;

        case MP_QSTR_sha256:
            esp_sha256_finish(&self->sha_context, (uint8_t *)self->buffer);
            esp_sha256_free(&self->sha_context);
            break;

        case MP_QSTR_sha384:
            esp_sha512_finish(&self->sha_context, (uint8_t *)self->buffer);
            esp_sha512_free(&self->sha_context);
            break;

        case MP_QSTR_sha512:
            esp_sha512_finish(&self->sha_context, (uint8_t *)self->buffer);
            esp_sha512_free(&self->sha_context);
            break;

        case MP_QSTR_md5:
            MD5Final((uint8_t *)self->buffer, &self->md5_context);
            break;
        }

        self->digested = true;
    }

    return mp_obj_new_bytes(self->buffer, self->h_size);
}

/******************************************************************************/
// Micro Python bindings

/// \classmethod \constructor([data])
/// initial data must be given if block_size wants to be passed
STATIC mp_obj_t hash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
    mp_arg_check_num(n_args, n_kw, 0, 1, false);

    mp_obj_hash_t *self = m_new_obj(mp_obj_hash_t);

    memset(self, 0, sizeof(mp_obj_hash_t));

    self->digested = false;
    self->base.type = type;

    switch (self->base.type->name) {
    case MP_QSTR_sha1:
        self->h_size = 20;
        self->b_size = 64;
        esp_sha1_init(&self->sha_context);
        esp_sha1_start(&self->sha_context);
        break;<commit_msg>esp32/mods/moduhashlib.c: limit hash operations to one at a time<commit_after>/*
 * Copyright (c) 2016, Pycom Limited.
 *
 * This software is licensed under the GNU GPL version 3 or any
 * later version, with permitted additional terms. For more information
 * see the Pycom Licence v1.0 document supplied with this file, or
 * available at https://www.pycom.io/opensource/licensing
 */

#include <assert.h>
#include <string.h>

#include "py/mpconfig.h"
#include "py/nlr.h"
#include "py/runtime.h"
#include "rom/md5_hash.h"
#include "hwcrypto/sha.h"
#include "mpexception.h"

STATIC bool hash_busy = false;

/******************************************************************************
 DEFINE PRIVATE TYPES
 ******************************************************************************/
typedef struct _mp_obj_hash_t {
    mp_obj_base_t base;
    uint8_t  b_size;
    uint8_t  h_size;
    uint8_t  buf_count;
    bool  digested;
    uint8_t buffer[128];
    union {
        struct MD5Context md5_context;
        esp_sha_context sha_context;
    };
} mp_obj_hash_t;


/******************************************************************************
 DECLARE PRIVATE FUNCTIONS
 ******************************************************************************/
STATIC void hash_update_internal(mp_obj_t self_in, mp_obj_t data, bool digest);
STATIC mp_obj_t hash_read (mp_obj_t self_in);

/******************************************************************************
 DEFINE PRIVATE FUNCTIONS
 ******************************************************************************/


STATIC void generic_hash_update(mp_obj_hash_t *self, void *data, uint32_t len) {
    switch (self->base.type->name) {
    case MP_QSTR_sha1:
        esp_sha1_update(&self->sha_context, data, len);
        break;

    case MP_QSTR_sha256:
        esp_sha256_update(&self->sha_context, data, len);
        break;

    case MP_QSTR_sha384:
    case MP_QSTR_sha512:
        esp_sha512_update(&self->sha_context, data, len);
        break;

    case MP_QSTR_md5:
        MD5Update(&self->md5_context, data, len);
        break;
    }
}

STATIC void hash_update_internal(mp_obj_t self_in, mp_obj_t data, bool digest) {
    mp_obj_hash_t *self = self_in;
    mp_buffer_info_t bufinfo;
    uint32_t len;

    if (digest == true) {
        generic_hash_update(self, self->buffer, self->buf_count);
        return;
    }

    if (!data) {
        return;
    }

    mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);

    // Try to fill the buffer to the top
    len = self->b_size - self->buf_count;
    len = MIN(len, bufinfo.len);

    memcpy(self->buffer + self->buf_count, bufinfo.buf, len);

    self->buf_count += len;

    // check if there is a complete block
    if (self->buf_count != self->b_size && digest == false) {
        return;
    }

    // process it
    generic_hash_update(self, self->buffer, self->buf_count);
    self->buf_count = 0;

    // update the input string information
    bufinfo.buf = ((uint8_t *) bufinfo.buf) + len;
    bufinfo.len -= len;

    // see if there is at least another complete block
    len = bufinfo.len / self->b_size;
    if (len != 0) {
        // process it (them)
        len *= self->b_size;
        generic_hash_update(self, bufinfo.buf, len);

        // update the input string information
        bufinfo.buf = ((uint8_t *) bufinfo.buf) + len;
        bufinfo.len -= len;
    }

    // and copy any remaining bytes to the buffer
    memcpy(self->buffer, bufinfo.buf, bufinfo.len);
    self->buf_count = bufinfo.len;
}

STATIC mp_obj_t hash_read(mp_obj_t self_in) {
    mp_obj_hash_t *self = self_in;

    if (!self->digested) {
        // process any remaining data in the buffer
        hash_update_internal(self, MP_OBJ_NULL, true);

        switch (self->base.type->name) {
        case MP_QSTR_sha1:
            esp_sha1_finish(&self->sha_context, (uint8_t *)self->buffer);
            esp_sha1_free(&self->sha_context);
            break;

        case MP_QSTR_sha256:
            esp_sha256_finish(&self->sha_context, (uint8_t *)self->buffer);
            esp_sha256_free(&self->sha_context);
            break;

        case MP_QSTR_sha384:
            esp_sha512_finish(&self->sha_context, (uint8_t *)self->buffer);
            esp_sha512_free(&self->sha_context);
            break;

        case MP_QSTR_sha512:
            esp_sha512_finish(&self->sha_context, (uint8_t *)self->buffer);
            esp_sha512_free(&self->sha_context);
            break;

        case MP_QSTR_md5:
            MD5Final((uint8_t *)self->buffer, &self->md5_context);
            break;
        }

        self->digested = true;
        hash_busy = false;
    }

    return mp_obj_new_bytes(self->buffer, self->h_size);
}

/******************************************************************************/
// Micro Python bindings

/// \classmethod \constructor([data])
/// initial data must be given if block_size wants to be passed
STATIC mp_obj_t hash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
    mp_arg_check_num(n_args, n_kw, 0, 1, false);


    if (hash_busy == true) {
        mp_raise_msg(&mp_type_OSError, "only one hash operation is permitted at a time");
    }

    hash_busy = true;

    mp_obj_hash_t *self = m_new_obj(mp_obj_hash_t);

    memset(self, 0, sizeof(mp_obj_hash_t));

    self->digested = false;
    self->base.type = type;

    switch (self->base.type->name) {
    case MP_QSTR_sha1:
        self->h_size = 20;
        self->b_size = 64;
        esp_sha1_init(&self->sha_context);
        esp_sha1_start(&self->sha_context);
        break;
{'fraction_non_alphanumeric': 0.20127214902317128, 'fraction_numerical': 0.01708314402544298, 'mean_word_length': 3.0793180133432174, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 107, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7186744', 'n_tokens_mistral': 3709, 'n_tokens_neox': 3473, 'n_words': 782}