sha1.c   [plain text]


/* crypto/sha/sha1.c */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@cryptsoft.com).
 * The implementation was written so as to conform with Netscapes SSL.
 * 
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are aheared to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
 * 
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from 
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 * 
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

/* 
 * In this odd implementation, the actual SHA1 code is in the sha_locl.h header.
 * Compile it exactly once, here.
 */
#define SHA_1   	1

#include <TargetConditionals.h>
#include <CommonCrypto/CommonDigest.h>

#if TARGET_OS_EMBEDDED && __arm__
#define CC_SHA1_USE_HARDWARE			1
#endif

#if CC_SHA1_USE_HARDWARE
#define CC_SHA1_USE_HARDWARE_THRESHOLD	4096
extern int _CC_SHA1_Update(CC_SHA1_CTX *c, const void *data, CC_LONG len);
#endif

#include "sha_locl.h"

#ifdef	_APPLE_COMMON_CRYPTO_

#if CC_SHA1_USE_HARDWARE
//Need the IOKitLib.h only to keep IOSHA1Types.h happy.
#include <IOKit/IOKitLib.h>
#include <Kernel/IOKit/crypto/IOSHA1Types.h>
#include <libkern/OSByteOrder.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <pthread.h>

static int cc_sha1_device = -1;
static pthread_once_t cc_sha1_connect_once = PTHREAD_ONCE_INIT;
static CC_LONG cc_sha1_hardware_quantum = (256*4096); //.25 M default value.

static void cc_sha1_connect(void) {
    struct IOSHA1AcceleratorInfo shaInfo;
	
	cc_sha1_device = open("/dev/sha1_0", O_RDWR, 0);
	if(cc_sha1_device < 0)
		return;

	if(ioctl(cc_sha1_device, IOSHA1_GET_INFO, &shaInfo) != -1) {
        cc_sha1_hardware_quantum = shaInfo.maxBytesPerCall;
	}
}

static CC_LONG sha1_hash_in_hardware(CC_SHA1_CTX *c, const UInt8 *data_buff, CC_LONG length, bool do_final)
{
	// Handle the hardware SHA1. 
	struct IOSHA1AcceleratorRequest shaRequest;
	CC_LONG quantum = cc_sha1_hardware_quantum;
	const UInt8 *data = data_buff;
	CC_LONG bytes_left = length;
	CC_LONG bytes_hashed = 0;
	
	//Load the saved context 
	shaRequest.hashBuffer.hashWords[0] = c->h0;
	shaRequest.hashBuffer.hashWords[1] = c->h1;
	shaRequest.hashBuffer.hashWords[2] = c->h2;
	shaRequest.hashBuffer.hashWords[3] = c->h3;
	shaRequest.hashBuffer.hashWords[4] = c->h4;
    shaRequest.options = 0;

	do {
		if (bytes_left < cc_sha1_hardware_quantum) {
            quantum = bytes_left;
            if (do_final) {
                shaRequest.options = kIOSHA1AcceleratorFinal;
				shaRequest.totalLength = (UInt64)(length) << 3; //Totallength is in bits.
            }
        } else {
            quantum = cc_sha1_hardware_quantum;
        }

		//Split the request in quantums if it is too large.
		shaRequest.sourceText = (UInt8 *)data;
		shaRequest.textLength = quantum;

		if(ioctl(cc_sha1_device, IOSHA1_PERFORM_HASH, &shaRequest) == -1) {
			break; //Failed to complete the whole request but fall back to the software only for the remaining bytes.
		}
		bytes_left -= quantum; 
		data += quantum;
	}while (bytes_left);

	bytes_hashed = (length - bytes_left); 
	if(bytes_hashed) {
		//Save the result in the CC_SHA1_CTX.
		c->h0 = shaRequest.hashBuffer.hashWords[0];
		c->h1 = shaRequest.hashBuffer.hashWords[1];
		c->h2 = shaRequest.hashBuffer.hashWords[2];
		c->h3 = shaRequest.hashBuffer.hashWords[3];
		c->h4 = shaRequest.hashBuffer.hashWords[4];

		//Update Nl and Nh in the context. Required to finish the hash.
		//Copied from the software SHA1 code. 
		CC_LONG l=(c->Nl+(bytes_hashed<<3))&0xffffffffL;
		if (l < c->Nl) /* overflow */
				c->Nh++;
		c->Nh+=(bytes_hashed>>29);
		c->Nl=l;
	}
	return bytes_hashed;
}

int CC_SHA1_Update(CC_SHA1_CTX *c, const void *data, CC_LONG len)
{
	const UInt8 *data_buff = (const UInt8 *) data;
	if (len > CC_SHA1_USE_HARDWARE_THRESHOLD &&
        !(((intptr_t)data_buff + CC_SHA1_BLOCK_BYTES - c->num) & 3) &&
        !pthread_once(&cc_sha1_connect_once, cc_sha1_connect) && cc_sha1_device >= 0) 
    {
		//USE SHA1 hardware.
		if(c->num) {
			//Do the first block or less in software
			CC_LONG partial = CC_SHA1_BLOCK_BYTES - c->num;
			_CC_SHA1_Update(c, data_buff, partial);
			len -= partial;
			data_buff += partial;
		}
		
		CC_LONG bytes_4_hardware = len & ~(CC_SHA1_BLOCK_BYTES - 1); //Send only mulitple of 64 bytes to the hardware.
		CC_LONG bytes_hashed = 0; 
		bytes_hashed = sha1_hash_in_hardware(c, data_buff, bytes_4_hardware, false);
        len -= bytes_hashed;
        data_buff += bytes_hashed;
	}

    //USE SHA1 software. If len is zero then this immediately returns;
    return _CC_SHA1_Update(c, data_buff, len);
}

UInt8* CC_SHA1(const void *data, CC_LONG len, UInt8 *md)
{	
	CC_LONG bytes_hashed = 0;
	const UInt8 *data_buff = (const UInt8 *)data;
	
	if(md == NULL)
		return NULL;									
		
	CC_SHA1_CTX ctx;
	CC_SHA1_Init(&ctx);
	
	if (len > CC_SHA1_USE_HARDWARE_THRESHOLD &&
        !((intptr_t)data_buff & 3) &&
        !pthread_once(&cc_sha1_connect_once, cc_sha1_connect) && cc_sha1_device >= 0) 
    {
		bytes_hashed = sha1_hash_in_hardware(&ctx, data_buff, len, true);
		if (bytes_hashed == len) {
            OSWriteBigInt32(md, 0, ctx.h0);
            OSWriteBigInt32(md, 4, ctx.h1);
            OSWriteBigInt32(md, 8, ctx.h2);
            OSWriteBigInt32(md, 12, ctx.h3);
            OSWriteBigInt32(md, 16, ctx.h4); 
			return md;
        }

		//Either we have failed partially or completely.
		//Fall through to the software.
		data_buff += bytes_hashed;
		len -= bytes_hashed;
	}
	//Fall back to Software SHA1.
	CC_SHA1_Update(&ctx, data_buff, len);
	CC_SHA1_Final(md, &ctx);					
	return md;												
}
#else //#if CC_SHA1_USE_HARDWARE
CC_DIGEST_ONE_SHOT(CC_SHA1, CC_SHA1_CTX, CC_SHA1_Init, CC_SHA1_Update, CC_SHA1_Final)
#endif

#endif //#ifdef	_APPLE_COMMON_CRYPTO_