SHA1Stream.st
author Claus Gittinger <cg@exept.de>
Tue, 09 Jul 2019 20:55:17 +0200
changeset 24417 03b083548da2
parent 23959 5e284faa1acc
permissions -rw-r--r--
#REFACTORING by exept class: Smalltalk class changed: #recursiveInstallAutoloadedClassesFrom:rememberIn:maxLevels:noAutoload:packageTop:showSplashInLevels: Transcript showCR:(... bindWith:...) -> Transcript showCR:... with:...

"{ Encoding: utf8 }"

"
 COPYRIGHT (c) 1999-2013 by eXept Software AG
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libbasic' }"

"{ NameSpace: Smalltalk }"

HashStream subclass:#SHA1Stream
	instanceVariableNames:'hashContext'
	classVariableNames:'HashSize ContextSize'
	poolDictionaries:''
	category:'System-Crypt-Hashing'
!

!SHA1Stream primitiveDefinitions!
%{

#if defined(__LSBFIRST) || defined(__LSBFIRST__)
# ifndef LITTLE_ENDIAN
#  define LITTLE_ENDIAN /* This should be #define'd if true. */
# endif
#endif

#define SHA1HANDSOFF /* Copies data before messing with it. */

#ifndef _STDIO_H_INCLUDED_
# include <stdio.h>
# define _STDIO_H_INCLUDED_
#endif

#ifndef _STRING_H_INCLUDED_
# include <string.h>
# define _STRING_H_INCLUDED_
#endif

typedef struct {
    unsigned int32 state[5];
    unsigned int32 count[2];
    unsigned char buffer[64];
} SHA1_CTX;

#if (defined(__GNUC__) || defined(__CLANG__))
# define STATIC_INLINE static inline
#else
# define STATIC_INLINE static
#endif

#if (defined(__GNUC__) || defined(__CLANG__))
# if (defined(__SSE4_1__) && defined(__SHA__))
#  if __SSE4_1__ && __SHA__
// # define __SSE4_1__ 1
// # define __SHA__    1
#   include <immintrin.h>
 static void SHA1Transform(unsigned int32 state[5], unsigned char buffer[64]);
 // static void __attribute__ ((__target__ ("sha,sse4.1"))) SHA1Transform_x86(unsigned int32 state[5], unsigned char buffer[64]);
 static void SHA1Transform_x86(unsigned int32 state[5], unsigned char buffer[64]);
#   define USE_SHA_INTRINSICS
#  endif
# endif
#endif

#ifndef USE_SHA_INTRINSICS
# define SHA1Transform SHA1Transform_generic
#endif

#if USE_ANSI_C

 static void SHA1Transform_generic(unsigned int32 state[5], unsigned char buffer[64]);
 STATIC_INLINE void SHA1Init(SHA1_CTX* context);
 void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len);
 void SHA1Final(unsigned char digest[20], SHA1_CTX* context);

#else /* general: compiles everywhere */

# define SHA1Init   __SHA1Init
# define SHA1Update __SHA1Update
# define SHA1Final  __SHA2Final

STATIC_INLINE void SHA1Init();
 void SHA1Update();
 void SHA1Final();

#endif /* USE_ANSI_C */

%}
! !

!SHA1Stream primitiveFunctions!
%{

/*
 * SHA-1 in C
 * By Steve Reid <steve@edmweb.com>
 * 100% Public Domain
 */

#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
// 876543210 -> rol,24 -> 108765432 -> & -> 10xx65xx
// 876543210 -> rol,8  -> 654321087 -> & -> xx43xx87
// oring: 10436587
#ifdef LITTLE_ENDIAN
// cg: does not make any difference (actually, slightly slower, as it seems)...
# if 0 && (defined(__i386__) || defined(__x86__) || defined(__x86_64__)) && (defined(__GNUC__) || defined(__CLANG__))
   static inline u_int32_t __bswap(u_int32_t v) {
	register u_int32_t l = v;
	__asm__ __volatile__("bswap %0" : "=r" (l) : "0" (l));
	return l;
   }
#  define blk0(i) \
    (block->l[i] = __bswap(block->l[i]))
# else
#  define blk0(i) \
    (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
		  |(rol(block->l[i],8)&0x00FF00FF))
# endif
#else
# define blk0(i) block->l[i]
#endif

#define blk(i) (block->l[i&15] = \
    rol(block->l[(i+13)&15] \
	^ block->l[(i+8)&15] \
	^ block->l[(i+2)&15] \
	^ block->l[i&15],1))

/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);

/*
 * Hash a single 512-bit block. This is the core of the algorithm.
 */
static void
#if USE_ANSI_C
SHA1Transform_generic (unsigned int32 state[5], unsigned char buffer[64])
#else
SHA1Transform_generic (state, buffer)
    unsigned int32 state[5];
    unsigned char buffer[64];
#endif
{
    unsigned int32 a, b, c, d, e;
    typedef union {
	unsigned char c[64];
	unsigned int32 /* long */ l[16];
    } CHAR64LONG16;
    CHAR64LONG16* block;
#ifdef SHA1HANDSOFF
    static unsigned char workspace[64];
    block = (CHAR64LONG16*)workspace;
    memcpy(block, buffer, 64);
#else
    block = (CHAR64LONG16*)buffer;
#endif
    /* Copy context->state[] to working vars */
    a = state[0];
    b = state[1];
    c = state[2];
    d = state[3];
    e = state[4];
    /* 4 rounds of 20 operations each. Loop unrolled. */
    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
    /* Add the working vars back into context.state[] */
    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;
    state[4] += e;
    /* Wipe variables */
    a = b = c = d = e = 0;
}

#if defined(USE_SHA_INTRINSICS)

//
// a specially tuned version
//
static void
SHA1Transform_x86 (unsigned int32 state[5], unsigned char buffer[64])
{
    unsigned int32 a, b, c, d, e;
    typedef union {
	unsigned char c[64];
	unsigned int32 /* long */ l[16];
    } CHAR64LONG16;
    CHAR64LONG16* block;
#ifdef SHA1HANDSOFF
    static unsigned char workspace[64];
    block = (CHAR64LONG16*)workspace;
    memcpy(block, buffer, 64);
#else
    block = (CHAR64LONG16*)buffer;
#endif
    __m128i ABCD, ABCD_SAVE, E0, E0_SAVE, E1;
    __m128i MSG0, MSG1, MSG2, MSG3;
    const __m128i MASK = _mm_set_epi64x(0x0001020304050607ULL, 0x08090a0b0c0d0e0fULL);

    /* Load initial values */
    ABCD = _mm_loadu_si128((const __m128i*) state);
    E0 = _mm_set_epi32(state[4], 0, 0, 0);
    ABCD = _mm_shuffle_epi32(ABCD, 0x1B);

	/* Save current state  */
	ABCD_SAVE = ABCD;
	E0_SAVE = E0;

	/* Rounds 0-3 */
	MSG0 = _mm_loadu_si128((const __m128i*)(block + 0));
	MSG0 = _mm_shuffle_epi8(MSG0, MASK);
	E0 = _mm_add_epi32(E0, MSG0);
	E1 = ABCD;
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);

	/* Rounds 4-7 */
	MSG1 = _mm_loadu_si128((const __m128i*)(block + 16));
	MSG1 = _mm_shuffle_epi8(MSG1, MASK);
	E1 = _mm_sha1nexte_epu32(E1, MSG1);
	E0 = ABCD;
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0);
	MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);

	/* Rounds 8-11 */
	MSG2 = _mm_loadu_si128((const __m128i*)(block + 32));
	MSG2 = _mm_shuffle_epi8(MSG2, MASK);
	E0 = _mm_sha1nexte_epu32(E0, MSG2);
	E1 = ABCD;
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
	MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
	MSG0 = _mm_xor_si128(MSG0, MSG2);

	/* Rounds 12-15 */
	MSG3 = _mm_loadu_si128((const __m128i*)(block + 48));
	MSG3 = _mm_shuffle_epi8(MSG3, MASK);
	E1 = _mm_sha1nexte_epu32(E1, MSG3);
	E0 = ABCD;
	MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0);
	MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
	MSG1 = _mm_xor_si128(MSG1, MSG3);

	/* Rounds 16-19 */
	E0 = _mm_sha1nexte_epu32(E0, MSG0);
	E1 = ABCD;
	MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
	MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
	MSG2 = _mm_xor_si128(MSG2, MSG0);

	/* Rounds 20-23 */
	E1 = _mm_sha1nexte_epu32(E1, MSG1);
	E0 = ABCD;
	MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
	MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
	MSG3 = _mm_xor_si128(MSG3, MSG1);

	/* Rounds 24-27 */
	E0 = _mm_sha1nexte_epu32(E0, MSG2);
	E1 = ABCD;
	MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1);
	MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
	MSG0 = _mm_xor_si128(MSG0, MSG2);

	/* Rounds 28-31 */
	E1 = _mm_sha1nexte_epu32(E1, MSG3);
	E0 = ABCD;
	MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
	MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
	MSG1 = _mm_xor_si128(MSG1, MSG3);

	/* Rounds 32-35 */
	E0 = _mm_sha1nexte_epu32(E0, MSG0);
	E1 = ABCD;
	MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1);
	MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
	MSG2 = _mm_xor_si128(MSG2, MSG0);

	/* Rounds 36-39 */
	E1 = _mm_sha1nexte_epu32(E1, MSG1);
	E0 = ABCD;
	MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
	MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
	MSG3 = _mm_xor_si128(MSG3, MSG1);

	/* Rounds 40-43 */
	E0 = _mm_sha1nexte_epu32(E0, MSG2);
	E1 = ABCD;
	MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
	MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
	MSG0 = _mm_xor_si128(MSG0, MSG2);

	/* Rounds 44-47 */
	E1 = _mm_sha1nexte_epu32(E1, MSG3);
	E0 = ABCD;
	MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2);
	MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
	MSG1 = _mm_xor_si128(MSG1, MSG3);

	/* Rounds 48-51 */
	E0 = _mm_sha1nexte_epu32(E0, MSG0);
	E1 = ABCD;
	MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
	MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
	MSG2 = _mm_xor_si128(MSG2, MSG0);

	/* Rounds 52-55 */
	E1 = _mm_sha1nexte_epu32(E1, MSG1);
	E0 = ABCD;
	MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2);
	MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
	MSG3 = _mm_xor_si128(MSG3, MSG1);

	/* Rounds 56-59 */
	E0 = _mm_sha1nexte_epu32(E0, MSG2);
	E1 = ABCD;
	MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
	MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
	MSG0 = _mm_xor_si128(MSG0, MSG2);

	/* Rounds 60-63 */
	E1 = _mm_sha1nexte_epu32(E1, MSG3);
	E0 = ABCD;
	MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
	MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
	MSG1 = _mm_xor_si128(MSG1, MSG3);

	/* Rounds 64-67 */
	E0 = _mm_sha1nexte_epu32(E0, MSG0);
	E1 = ABCD;
	MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3);
	MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
	MSG2 = _mm_xor_si128(MSG2, MSG0);

	/* Rounds 68-71 */
	E1 = _mm_sha1nexte_epu32(E1, MSG1);
	E0 = ABCD;
	MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
	MSG3 = _mm_xor_si128(MSG3, MSG1);

	/* Rounds 72-75 */
	E0 = _mm_sha1nexte_epu32(E0, MSG2);
	E1 = ABCD;
	MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
	ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3);

	/* Rounds 76-79 */
	E1 = _mm_sha1nexte_epu32(E1, MSG3);
	E0 = ABCD;
	ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);

	/* Combine state */
	E0 = _mm_sha1nexte_epu32(E0, E0_SAVE);
	ABCD = _mm_add_epi32(ABCD, ABCD_SAVE);

    /* Save state */
    ABCD = _mm_shuffle_epi32(ABCD, 0x1B);
    _mm_storeu_si128((__m128i*) state, ABCD);
    state[4] = _mm_extract_epi32(E0, 3);
}

static void
#if USE_ANSI_C
SHA1Transform(unsigned int32 state[5], unsigned char buffer[64])
#else
SHA1Transform(state, buffer)
    unsigned int32 state[5];
    unsigned char buffer[64];
#endif
{
    extern unsigned char __cpu_hasSSE4_1_and_SHA;

    if (__cpu_hasSSE4_1_and_SHA) {
	SHA1Transform_x86(state, buffer);
    } else {
	SHA1Transform_generic(state, buffer);
    }
}

#endif

/*
 * SHA1Init - Initialize new context
 */
STATIC_INLINE void
#if USE_ANSI_C
SHA1Init(SHA1_CTX* context)
#else
SHA1Init(context)
    SHA1_CTX* context;
#endif
{
    /* SHA1 initialization constants */
    context->state[0] = 0x67452301;
    context->state[1] = 0xEFCDAB89;
    context->state[2] = 0x98BADCFE;
    context->state[3] = 0x10325476;
    context->state[4] = 0xC3D2E1F0;
    context->count[0] = context->count[1] = 0;
}


/*
 * Run your data through this.
 */
void
#if USE_ANSI_C
SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len)
#else
SHA1Update(context, data, len)
    SHA1_CTX* context;
    unsigned char* data;
    unsigned int len;
#endif
{
    unsigned int i, j;

    j = (context->count[0] >> 3) & 63;
    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
    context->count[1] += (len >> 29);
    if ((j + len) > 63) {
	memcpy(&context->buffer[j], data, (i = 64-j));
	SHA1Transform(context->state, context->buffer);
	for ( ; i + 63 < len; i += 64) {
	    SHA1Transform(context->state, &data[i]);
	}
	j = 0;
    }
    else i = 0;
    memcpy(&context->buffer[j], &data[i], len - i);
}


/*
 * Add padding and return the message digest.
 */
void
#if USE_ANSI_C
SHA1Final(unsigned char digest[20], SHA1_CTX* context)
#else
SHA1Final(digest, context)
    unsigned char digest[20];
    SHA1_CTX* context;
#endif
{
    unsigned int32 i, j;
    unsigned char finalcount[8];

    for (i = 0; i < 8; i++) {
	finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
	 >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
    }
    SHA1Update(context, (unsigned char *)"\200", 1);
    while ((context->count[0] & 504 /* 16r1F8 */) != 448 /* 16r1C0 */) {
	SHA1Update(context, (unsigned char *)"\0", 1);
    }
    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
    for (i = 0; i < 20; i++) {
	digest[i] = (unsigned char)
	 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
    }
    /* Wipe variables */
    i = j = 0;
    memset(context->buffer, 0, 64);
    memset(context->state, 0, 20);
    memset(context->count, 0, 8);
    memset(&finalcount, 0, 8);
#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
    SHA1Transform(context->state, context->buffer);
#endif
}

#if 0
/*************************************************************/

/*
 * SHA1 test program
 */

int main(int argc, char** argv)
{
    int i, j;
    SHA1_CTX context;
    unsigned char digest[20], buffer[16384];
    FILE* file;

    if (argc > 2) {
	puts("Public domain SHA-1 implementation - by Steve Reid <steve@edmweb.com>");
	puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
	exit(0);
    }
    if (argc < 2) {
	file = stdin;
    }
    else {
	if (!(file = fopen(argv[1], "rb"))) {
	    fputs("Unable to open file.", stderr);
	    exit(-1);
	}
    }
    SHA1Init(&context);
    while (!feof(file)) {  /* note: what if ferror(file) */
	i = fread(buffer, 1, 16384, file);
	SHA1Update(&context, buffer, i);
    }
    SHA1Final(digest, &context);
    fclose(file);
    for (i = 0; i < 5; i++) {
	for (j = 0; j < 4; j++) {
	    printf("%02X", digest[i*4+j]);
	}
	putchar(' ');
    }
    putchar('\n');
    exit(0);
}
#endif

%}
! !

!SHA1Stream class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1999-2013 by eXept Software AG
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    Generate a SHA-1 hash value as defined in
    NIST, FIPS PUB 180-1: Secure Hash Standard, April 1995.

    Notice (2005):
	Be aware that SHA-1 is considered broken and may not be appropriate in some applications.
	Especially it should no longer be used for security stuff.

    performance: roughly
	  200 Mb/s on a 2012 MAC Powerbook (2.6Ghz I7)
	  150 Mb/s on a 2007 MAC Powerbook (2.6Ghz Duo)
	  120 Mb/s on a 2.5Ghz 64X2 Athlon 4800+ (64bit)
	   47400 Kb/s on a 2Ghz Duo (old measure)
	    9580 Kb/s on a 400Mhz PIII
	    3970 Kb/s on a 300Mhz Sparc

    [author:]
	Stefan Vogel

    [see also:]
	MD5Stream
	SHA256Stream SHA512Stream (in libcrypt)

    [class variables:]
	HashSize        size of returned hash value
	ContextSize     (implementation) size of hash context

    [instance variables:]
	hashContext     (implementation)
			internal buffer for computation of the hash value
"
!

examples
"
    Test Vectors (from FIPS PUB 180-1); results are:

								[exBegin]
    |hashStream|

    hashStream := SHA1Stream new.
    hashStream nextPut:'abc'.
    hashStream hashValue printOn:Transcript base:16. Transcript cr.
    hashStream nextPut:'dbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'.
    hashStream hashValue printOn:Transcript base:16. Transcript cr.
								[exEnd]

								[exBegin]
    |hashValue|

    hashValue := SHA1Stream hashValueOf:'hello world'.
    self assert:(hashValue hexPrintString = '2AAE6C35C94FCFB415DBE95F408B9CE91EE846ED')
								[exEnd]

								[exBegin]
    |hashValue|

    hashValue := SHA1Stream hashValueOf:'abc'.
    hashValue printOn:Transcript base:16. Transcript cr.
								[exEnd]

								[exBegin]
    |hashStream|

    hashStream := SHA1Stream new.
    hashStream nextPut:'abc' asByteArray.
    hashStream hashValue printOn:Transcript base:16. Transcript cr.
    hashStream nextPut:'dbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq' asByteArray.
    hashStream hashValue printOn:Transcript base:16. Transcript cr.

								[exEnd]

								[exBegin]
    |hashStream|

    hashStream := SHA1Stream new.
    1000000 timesRepeat:[ hashStream nextPut:$a ].
    hashStream hashValue printOn:Transcript base:16. Transcript cr.
								[exEnd]

								[exBegin]
    |hashStream|

    hashStream := SHA1Stream new.
    hashStream nextPut:'a'.
    hashStream hashValue printOn:Transcript base:16. Transcript cr.
								[exEnd]

								[exBegin]
    |hashStream|

    hashStream := SHA1Stream new.
    hashStream nextPut:$a.
    hashStream hashValue printOn:Transcript base:16. Transcript cr.
								[exEnd]

  timing throughput:
								[exBegin]
    |hashStream n t|

    hashStream := SHA1Stream new.
    n := 1000000.
    t := Time millisecondsToRun:[
	    n timesRepeat:[
		hashStream nextPutAll:'12345678901234567890123456789012345678901234567890'.
	    ].
	 ].
    t := (t / 1000) asFloat.
    Transcript show:t; show:' seconds for '; show:(50*n/1024) asFloat; showCR:' Kb'.
    Transcript show:(n*50/1024 / t); showCR:' Kb/s'
								[exEnd]
"
!

performance
"
    CPU                             cc          algo        mb/sec

    MAC (2010 macbook; 2.7Ghz Duo)  clang -O2   slow        128.5
							    132
    MAC (2012 macbook; 2.6Ghz I7)   clang -O2               190


    chunk size 10:     86.70 Mb/s   90.83
    chunk size 50:    227.07 Mb/s  238.42
    chunk size 1000:  405.82 Mb/s  414.64
    chunk size 50000: 421.98 Mb/s  447.73


  timing throughput:
								[exBegin]
    |hashStream n t|

    hashStream := SHA1Stream new.
    n := 1000000.
    t := Time millisecondsToRun:[
	    n timesRepeat:[
		hashStream nextPutAll:'12345678901234567890123456789012345678901234567890'.
	    ].
	 ].
    t := (t / 1000) asFloat.
    Transcript show:t; show:' seconds for '; show:(50*n/1024) asFloat; showCR:' Kb'.
    Transcript show:(n*50/1024 / t); showCR:' Kb/s'
								[exEnd]
"
! !

!SHA1Stream class methodsFor:'initialization'!

initialize
    |ctxSize|

%{
    ctxSize = __MKSMALLINT(sizeof(SHA1_CTX));
%}.
    ContextSize := ctxSize.
    HashSize := 20.

    "
	self initialize
    "
! !

!SHA1Stream class methodsFor:'queries'!

hashBlockSize
    "return the block size used internally by the compression function"

    ^ 64

    "Created: / 18.3.1999 / 08:37:10 / stefan"
!

hashSize
    "return the size of the hashvalue returned by instances of this class"

    ^ HashSize

    "Modified: / 18.3.1999 / 07:54:22 / stefan"
! !

!SHA1Stream methodsFor:'initialization'!

initialize

    hashContext := ByteArray new:ContextSize.
    self reset

    "Created: / 17.3.1999 / 16:11:37 / stefan"
    "Modified: / 18.3.1999 / 07:56:46 / stefan"
! !

!SHA1Stream methodsFor:'positioning'!

reset
   "reset the stream in order to compute a new hash value"

%{
   if (__isByteArray(__INST(hashContext)) &&
       __byteArraySize(__INST(hashContext)) == sizeof(SHA1_CTX)
   ) {
	SHA1_CTX *ctx = (SHA1_CTX *)__ByteArrayInstPtr(__INST(hashContext))->ba_element;

	SHA1Init(ctx);
	RETURN(self);
   }
%}.
   ^ self primitiveFailed

    "Created: / 18-03-1999 / 07:59:02 / stefan"
    "Modified: / 12-01-2012 / 12:21:23 / cg"
!

seedWith:fiveWordVector
   "seed the stream with 5*16  (for testing)"

    |seedA seedB seedC seedD seedE|

    seedA := fiveWordVector at:1.
    seedB := fiveWordVector at:1.
    seedC := fiveWordVector at:1.
    seedD := fiveWordVector at:1.
    seedE := fiveWordVector at:1.
    self reset.
%{
   if (__isByteArray(__INST(hashContext))
    && (__byteArraySize(__INST(hashContext)) == sizeof(SHA1_CTX))
    && __isSmallInteger(seedA)
    && __isSmallInteger(seedB)
    && __isSmallInteger(seedC)
    && __isSmallInteger(seedD)
    && __isSmallInteger(seedE)
   ) {
	SHA1_CTX *ctx = (SHA1_CTX *)__ByteArrayInstPtr(__INST(hashContext))->ba_element;

	ctx->state[0] = __intVal(seedA);
	ctx->state[1] = __intVal(seedB);
	ctx->state[2] = __intVal(seedC);
	ctx->state[3] = __intVal(seedD);
	ctx->state[4] = __intVal(seedE);
	RETURN(self);
   }
%}.
   ^ self primitiveFailed
! !

!SHA1Stream methodsFor:'queries'!

hashValue
    "Get the value hashed so far.
     The context is kept, so that more objects may be hashed after
     retrieving a hash value"


    |digest|

    digest := ByteArray new:HashSize.

%{
    OBJ hcon;

    hcon = __INST(hashContext);
    if (__isByteArray(hcon) &&
	__byteArraySize(hcon) == sizeof(SHA1_CTX) &&
	__isByteArray(digest) &&
	__byteArraySize(digest) == 20
    ) {
	SHA1_CTX *ctx = (SHA1_CTX *)(__ByteArrayInstPtr(hcon)->ba_element);
	SHA1_CTX copyContext;

	memcpy(&copyContext, ctx, sizeof(SHA1_CTX));
	SHA1Final(__ByteArrayInstPtr(digest)->ba_element, &copyContext);
	RETURN(digest);
    }
%}.

    ^ self primitiveFailed

    "Created: / 17.3.1999 / 16:13:12 / stefan"
    "Modified: / 18.3.1999 / 08:00:54 / stefan"
! !

!SHA1Stream methodsFor:'writing'!

nextPutByte:anInteger
    "update the hash value with anInteger <= 255."

%{
   unsigned char value;
   OBJ _hashContext = __INST(hashContext);

   // fetch first; check below
   value = __intVal(anInteger);
   if (__isSmallInteger(anInteger) && value <= 255
       && __isByteArray(_hashContext)
       && __byteArraySize(_hashContext) == sizeof(SHA1_CTX)
   ) {
	SHA1_CTX *ctx = (SHA1_CTX *)__byteArrayVal(_hashContext);

	SHA1Update(ctx, &value, 1);
	RETURN(self);
    }
bad: ;
%}.

    ^ self primitiveFailed

    "Modified: / 23-03-2019 / 15:19:40 / Claus Gittinger"
!

nextPutBytes:count from:anObject startingAt:start
    "update the hash value with count bytes from an object starting at index start.
     The object must have non-pointer indexed instvars
     (i.e. be a ByteArray, String, Float- or DoubleArray),
     or an externalBytes object (with known size)"

%{
    INT len, offs;
    INT objSize;
    int nInstBytes;
    unsigned char *extPtr;
    OBJ _hashContext = __INST(hashContext);

    // convert here; check later
    len = __intVal(count);
    offs = __intVal(start) - 1;

    if (__isByteArray(_hashContext)
       && __byteArraySize(_hashContext) == sizeof(SHA1_CTX)
       && __bothSmallInteger(count, start)
    ) {
	SHA1_CTX *ctx = (SHA1_CTX *)__byteArrayVal(_hashContext);

	if (__isByteArrayLike(anObject)) {
	    extPtr = (unsigned char *)__byteArrayVal(anObject);
	    objSize = __byteArraySize(anObject);
	} else if (__isStringLike(anObject)) {
	    extPtr = (unsigned char *)__stringVal(anObject);
	    objSize = __stringSize(anObject);
	} else if (__isExternalBytesLike(anObject)) {
	    OBJ sz;

	    nInstBytes = 0;
	    extPtr = (unsigned char *)__externalBytesAddress(anObject);
	    if (extPtr == NULL) goto bad;
	    sz = __externalBytesSize(anObject);
	    objSize = __intVal(sz);
	    if (!__isSmallInteger(sz)) {
		objSize = 0; /* unknown */
	    }
	} else {
	    OBJ oClass = __Class(anObject);
	    int nInstVars = __intVal(__ClassInstPtr(oClass)->c_ninstvars);

	    nInstBytes = OHDR_SIZE + __OBJS2BYTES__(nInstVars);
	    switch (__intVal(__ClassInstPtr(oClass)->c_flags) & ARRAYMASK) {
		case BYTEARRAY:
		case WORDARRAY:
		case LONGARRAY:
		case SWORDARRAY:
		case SLONGARRAY:
		case FLOATARRAY:
		    break;
		case DOUBLEARRAY:
#ifdef __NEED_DOUBLE_ALIGN
		    nInstBytes = (nInstBytes-1+__DOUBLE_ALIGN) &~ (__DOUBLE_ALIGN-1);
#endif
		    break;
		case LONGLONGARRAY:
		case SLONGLONGARRAY:
#ifdef __NEED_LONGLONG_ALIGN
		    nInstBytes = (nInstBytes-1+__LONGLONG_ALIGN) &~ (__LONGLONG_ALIGN-1);
#endif
		    break;
		default:
		    goto bad;
	    }
	    // nInstBytes is the number of bytes occupied by pointer instance variables
	    // subtract from size and add to byte-pointer
	    objSize = __Size(anObject) - nInstBytes;
	    extPtr = (unsigned char *)anObject + nInstBytes;
	}
	if ((offs >= 0) && (len >= 0) && (objSize >= (len + offs))) {
	    SHA1Update(ctx, extPtr+offs, (unsigned int)len);
	    RETURN (count);
	}
    }
bad: ;
%}.

    ^ self primitiveFailed

    "Modified: / 23-03-2019 / 15:28:31 / Claus Gittinger"
! !

!SHA1Stream class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !


SHA1Stream initialize!