Login

Subversion Repositories NedoOS

Rev

Blame | Last modification | View Log | Download | RSS feed

// hashplay framework
// (c) 2022 lvd^mhm

/*
    This file is part of hashplay framework.

    hashplay framework is free software:
    you can redistribute it and/or modify it under the terms of
    the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    hashplay framework is distributed in the hope that
    it will be useful, but WITHOUT ANY WARRANTY; without even
    the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with hashplay framework.
    If not, see <http://www.gnu.org/licenses/>.
*/


#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "hash-common.h"
#include "sphash-opt.h"


#define DIGEST_SIZE 8

#define BLK_SIZE 32

static const uint32_t x0 = 0x01180011; // hex-encoded numbers 71680, 70908, 69888
static const uint32_t y0 = 0x4fc11100; //


// instantiate speck_uint32_t_8_29




#define ROL(w,n) (((w)<<(n))|((w)>>32-(n)))

#define ROUND(x,y,k)           \
        do {                   \
                x = ROL(x,24); \
                x += y;        \
                x ^= k;        \
                y = ROL(y,3);  \
                y ^= x;        \
        } while(0)





struct my_sphash
{
        uint64_t pos;

        uint8_t buf[BLK_SIZE];

        uint32_t state[2];
};


static void sphash_compress( const uint32_t * m, uint32_t * state)
{
        uint32_t x,y;
        uint32_t k,k1,k2,k3,k4,k5,k6,k7;

        x = state[0];
        y = state[1];

        //speck_encrypt_uint32_t_8_29(tmp,m);
        k = m[0];
        k1 = m[1];
        k2 = m[2];
        k3 = m[3];
        k4 = m[4];
        k5 = m[5];
        k6 = m[6];
        k7 = m[7];

        ROUND(x,y,k); ROUND(k1,k, 0);
        ROUND(x,y,k); ROUND(k2,k, 1);
        ROUND(x,y,k); ROUND(k3,k, 2);
        ROUND(x,y,k); ROUND(k4,k, 3);
        ROUND(x,y,k); ROUND(k5,k, 4);
        ROUND(x,y,k); ROUND(k6,k, 5);
        ROUND(x,y,k); ROUND(k7,k, 6);

        ROUND(x,y,k); ROUND(k1,k, 7);
        ROUND(x,y,k); ROUND(k2,k, 8);
        ROUND(x,y,k); ROUND(k3,k, 9);
        ROUND(x,y,k); ROUND(k4,k,10);
        ROUND(x,y,k); ROUND(k5,k,11);
        ROUND(x,y,k); ROUND(k6,k,12);
        ROUND(x,y,k); ROUND(k7,k,13);
       
        ROUND(x,y,k); ROUND(k1,k,14);
        ROUND(x,y,k); ROUND(k2,k,15);
        ROUND(x,y,k); ROUND(k3,k,16);
        ROUND(x,y,k); ROUND(k4,k,17);
        ROUND(x,y,k); ROUND(k5,k,18);
        ROUND(x,y,k); ROUND(k6,k,19);
        ROUND(x,y,k); ROUND(k7,k,20);
       
        ROUND(x,y,k); ROUND(k1,k,21);
        ROUND(x,y,k); ROUND(k2,k,22);
        ROUND(x,y,k); ROUND(k3,k,23);
        ROUND(x,y,k); ROUND(k4,k,24);
        ROUND(x,y,k); ROUND(k5,k,25);
        ROUND(x,y,k); ROUND(k6,k,26);
        ROUND(x,y,k); ROUND(k7,k,27);
       
        ROUND(x,y,k);

        state[0] += x;
        state[1] += y;
}



static inline size_t my_min(size_t a, size_t b)
{
        if( a<=b ) return a; else return b;
}

struct hash_iface * make_sphash_opt(void)
{
        static const char name[]="speck-based hash: sphash64, optimized impl.";

        struct hash_iface * hash = malloc(sizeof(struct hash_iface));
        if( !hash )
        {
                fprintf(stderr,"%s: %d, %s: can't allocate memory for hash_iface!\n",__FILE__,__LINE__,__func__);
                exit(1);
        }

        hash->hash_specific_data = NULL;

        hash->name = name;

        hash->hash_init     = &sphash_opt_hash_init;
        hash->hash_start    = &sphash_opt_hash_start;
        hash->hash_addbytes = &sphash_opt_hash_addbytes;
        hash->hash_getsize  = &sphash_opt_hash_getsize;
        hash->hash_result   = &sphash_opt_hash_result;
        hash->hash_deinit   = &sphash_opt_hash_deinit;

        return hash;
}

int    sphash_opt_hash_init    (struct hash_iface * hash)
{
        struct my_sphash * sphash = (struct my_sphash *)malloc(sizeof(struct my_sphash));
       
        if( !sphash )
        {
                fprintf(stderr,"%s: %d, %s: can't allocate memory for struct my_sphash!\n",__FILE__,__LINE__,__func__);
                exit(1);
        }

        hash->hash_specific_data = (void *)sphash;

        return 1;
}

void   sphash_opt_hash_deinit  (struct hash_iface * hash)
{
        struct my_sphash * sphash = (struct my_sphash *)hash->hash_specific_data;
       
        if( !sphash )
        {
                fprintf(stderr,"%s: %d, %s: hash_specific_data was NULL!\n",__FILE__,__LINE__,__func__);
                exit(1);
        }

        free(sphash);
       
        hash->hash_specific_data = NULL;
}

size_t sphash_opt_hash_getsize (struct hash_iface * hash)
{
        return DIGEST_SIZE;
}

int    sphash_opt_hash_start   (struct hash_iface * hash)
{
        struct my_sphash * sphash = (struct my_sphash *)hash->hash_specific_data;
       
        if( !sphash )
        {
                fprintf(stderr,"%s: %d, %s: hash_specific_data was NULL!\n",__FILE__,__LINE__,__func__);
                exit(1);
        }

        sphash->pos = 0;

        for(int i=0;i<BLK_SIZE;i++)
                sphash->buf[i] = 0;

        sphash->state[0] = x0;
        sphash->state[1] = y0;

        return 1;
}

int    sphash_opt_hash_addbytes(struct hash_iface * hash, const uint8_t * message, size_t size)
{
        struct my_sphash * sphash = (struct my_sphash *)hash->hash_specific_data;
       
        if( !sphash )
        {
                fprintf(stderr,"%s: %d, %s: hash_specific_data was NULL!\n",__FILE__,__LINE__,__func__);
                exit(1);
        }

        const uint8_t * ptr = message;
        size_t remaining_size = size;


        while( remaining_size > 0 )
        {
                // do full buffers
                if( !(sphash->pos & (BLK_SIZE-1)) )
                {
                        if( remaining_size>=BLK_SIZE )
                        {
                                sphash_compress( (uint32_t *)ptr, sphash->state );

                                sphash->pos += BLK_SIZE;
                                ptr += BLK_SIZE;
                                remaining_size -= BLK_SIZE;

                                continue;
                        }
                }


                // otherwise fill with all available data up to the end of buffer, if possible
                size_t size_to_add = my_min( (BLK_SIZE-(sphash->pos & (BLK_SIZE-1))), remaining_size );

                do
                {
                        sphash->buf[sphash->pos & (BLK_SIZE-1)] = *ptr;

                        ptr++;
                        sphash->pos++;
                        remaining_size--;

                } while( (--size_to_add) );

                if( !(sphash->pos & (BLK_SIZE-1)) )
                        sphash_compress( (uint32_t *)sphash->buf, sphash->state );
        }

        return 1;
}

int    sphash_opt_hash_result  (struct hash_iface * hash, uint8_t * result)
{
        struct my_sphash * sphash = (struct my_sphash *)hash->hash_specific_data;
       
        if( !sphash )
        {
                fprintf(stderr,"%s: %d, %s: hash_specific_data was NULL!\n",__FILE__,__LINE__,__func__);
                exit(1);
        }


        /*
         * We're gonna using Nandi padding rule, as per following paper:
         *
         * ===
         * Nandi M. (2009) Characterizing Padding Rules of MD Hash Functions Preserving Collision Security.
         * In: Boyd C., Gonz├бlez Nieto J. (eds) Information Security and Privacy. ACISP 2009.
         * Lecture Notes in Computer Science, vol 5594. Springer, Berlin, Heidelberg.
         * https://doi.org/10.1007/978-3-642-02620-1_12
         *
         * also https://eprint.iacr.org/2009/325.pdf
         * or use sci-hub.
         * ===
         *
         * Specifically, the padding is as per section 3.2, remark 2, s=16
         */



        uint8_t padding[BLK_SIZE+8]; // suitable for up to 4 chunks of 16 bits, total 4*15 = 60 bits
                                     // for length IN BLOCKS. Block is BLK_SIZE (min 32 bytes), so
                                     // 2^64 / 32 = 2^59.

        for(int i=0;i<sizeof(padding);i++)
                padding[i]=0;
       
        uint64_t blk_size = (sphash->pos+BLK_SIZE-1)/BLK_SIZE; // number of blocks incl. partially filled last one

        int num_chunks = 1;
        uint64_t t = blk_size;
        while( t>>=15 ) num_chunks++;

        // fill length part of padding
        t = blk_size;
        for(int i=num_chunks-1;i>=0;i--)
        {
                uint16_t chunk = (t & 0x7FFF) | (i ? 0x8000 : 0);

                padding[BLK_SIZE+i*2  ] = chunk>>8;
                padding[BLK_SIZE+i*2+1] = chunk & 0xFF;

                t>>=15;
        }





        int num_pad_bytes = (BLK_SIZE-2*num_chunks) - (sphash->pos & (BLK_SIZE-1));

        if( num_pad_bytes<=0 ) num_pad_bytes += BLK_SIZE;

        padding[BLK_SIZE-num_pad_bytes] = 0x80;

        sphash_opt_hash_addbytes(hash, &padding[BLK_SIZE-num_pad_bytes], 2*num_chunks+num_pad_bytes);

        assert( !(sphash->pos & (BLK_SIZE-1)) );


        for(int i=0;i<DIGEST_SIZE/4;i++)
        {
                *(((uint32_t *)result)+i) = sphash->state[i];
        }

        return 1;
}