DigestTest.mm   [plain text]


//
//  DigestTest.mm
//  CommonCrypto
//
//  Created by Jim Murphy on 1/12/10.
//  Copyright 2010 Apple. All rights reserved.
//

#import "DigestTest.h"
#import "RandomNumberService.h"
#include "CommonDigest.h"
#include "CommonDigestSPI.h"
#include <stdio.h>


@implementation CCDigestTestObject

@synthesize digestName = _digestName;
@synthesize digestSize = _digestSize;
@synthesize stagedResult = _stagedResult;
@synthesize oneShotResult = _oneShotResult;
@synthesize digestData = _digestData;
@synthesize testObject = _testObject;
@synthesize testPassed = _testPassed;


/* --------------------------------------------------------------------------
	method: 		setupDigestTests
	returns: 		NSArray *												
	decription: 	This method allows for creating digest specific tests for
					all of the digest supported by the CommonCrypto library.
					It creates an instance of the CCDigestTestObject for
					each digest to be tested and places that object into
					an NSArray.
   -------------------------------------------------------------------------- */
+ (NSArray *)setupDigestTests:(id<TestToolProtocol>)testObject;
{
	initBlock anInitBlock;
	updateBlock anUpdateBlock;
	finalBlock aFinalBlock;
	oneShotBlock anOneShotBlock;
	
	NSMutableArray* result = [NSMutableArray array]; // autoreleased
	
	// --------------------- MD2 Digest ----------------------
	anInitBlock = ^(void *ctx)
	{
		return CC_MD2_Init((CC_MD2_CTX *)ctx);
	};
	
	anUpdateBlock = ^(void *ctx, const void *data, CC_LONG len)
	{
		return CC_MD2_Update((CC_MD2_CTX *)ctx, data, len);
	};
	
	aFinalBlock = ^(unsigned char *md, void *ctx)
	{
		return CC_MD2_Final(md, (CC_MD2_CTX *)ctx);
	};
	
	anOneShotBlock = ^(const void *data, CC_LONG len, unsigned char *md)
	{
		return CC_MD2(data, len, md);
	};
	
	CCDigestTestObject* md2DigestTest = [[[CCDigestTestObject alloc] 
		initWithDigestName:@"MD2" 
		withDigestSize:CC_MD2_DIGEST_LENGTH 
		withInitBlock:anInitBlock
		withUpdateBlock:anUpdateBlock
		withFinalBlock:aFinalBlock
		withOneShotBlock:anOneShotBlock] autorelease];
		
	md2DigestTest.testObject = testObject;
		
	[result addObject:md2DigestTest];
	
	// --------------------- MD4 Digest ----------------------
	anInitBlock = ^(void *ctx)
	{
		return CC_MD4_Init((CC_MD4_CTX *)ctx);
	};
	
	anUpdateBlock = ^(void *ctx, const void *data, CC_LONG len)
	{
		return CC_MD4_Update((CC_MD4_CTX *)ctx, data, len);
	};
	
	aFinalBlock = ^(unsigned char *md, void *ctx)
	{
		return CC_MD4_Final(md, (CC_MD4_CTX *)ctx);
	};
	
	anOneShotBlock = ^(const void *data, CC_LONG len, unsigned char *md)
	{
		return CC_MD4(data, len, md);
	};
	
	CCDigestTestObject* md4DigestTest = [[[CCDigestTestObject alloc] initWithDigestName:@"MD4" 
		withDigestSize:CC_MD4_DIGEST_LENGTH 
		withInitBlock:anInitBlock
		withUpdateBlock:anUpdateBlock
		withFinalBlock:aFinalBlock
		withOneShotBlock:anOneShotBlock] autorelease];
		
	md4DigestTest.testObject = testObject;
		
	[result addObject:md4DigestTest];
	
	// --------------------- MD5 Digest ----------------------
	
	anInitBlock = ^(void *ctx)
	{
		return CC_MD5_Init((CC_MD5_CTX *)ctx);
	};
	
	anUpdateBlock = ^(void *ctx, const void *data, CC_LONG len)
	{
		return CC_MD5_Update((CC_MD5_CTX *)ctx, data, len);
	};
	
	aFinalBlock = ^(unsigned char *md, void *ctx)
	{
		return CC_MD5_Final(md, (CC_MD5_CTX *)ctx);
	};
	
	anOneShotBlock = ^(const void *data, CC_LONG len, unsigned char *md)
	{
		return CC_MD5(data, len, md);
	};
	
	CCDigestTestObject* md5DigestTest = [[[CCDigestTestObject alloc] initWithDigestName:@"MD5" 
		withDigestSize:CC_MD5_DIGEST_LENGTH 
		withInitBlock:anInitBlock
		withUpdateBlock:anUpdateBlock
		withFinalBlock:aFinalBlock
		withOneShotBlock:anOneShotBlock] autorelease];
		
	md5DigestTest.testObject = testObject;
		
	[result addObject:md5DigestTest];
	
	// --------------------- SHA1 Digest ----------------------
		
	anInitBlock = ^(void *ctx)
	{
		return CC_SHA1_Init((CC_SHA1_CTX *)ctx);
	};
	
	anUpdateBlock = ^(void *ctx, const void *data, CC_LONG len)
	{
		return CC_SHA1_Update((CC_SHA1_CTX *)ctx, data, len);
	};
	
	aFinalBlock = ^(unsigned char *md, void *ctx)
	{
		return CC_SHA1_Final(md, (CC_SHA1_CTX *)ctx);
	};
	
	anOneShotBlock = ^(const void *data, CC_LONG len, unsigned char *md)
	{
		return CC_SHA1(data, len, md);
	};
	
	CCDigestTestObject* sha1DigestTest = [[[CCDigestTestObject alloc] initWithDigestName:@"SHA1" 
		withDigestSize:CC_SHA1_DIGEST_LENGTH 
		withInitBlock:anInitBlock
		withUpdateBlock:anUpdateBlock
		withFinalBlock:aFinalBlock
		withOneShotBlock:anOneShotBlock] autorelease];
		
	sha1DigestTest.testObject = testObject;
		
	[result addObject:sha1DigestTest];
	
	// --------------------- SHA224 Digest ----------------------
	
	anInitBlock = ^(void *ctx)
	{
		return CC_SHA224_Init((CC_SHA256_CTX *)ctx);
	};
	
	anUpdateBlock = ^(void *ctx, const void *data, CC_LONG len)
	{
		return CC_SHA224_Update((CC_SHA256_CTX *)ctx, data, len);
	};
	
	aFinalBlock = ^(unsigned char *md, void *ctx)
	{
		return CC_SHA224_Final(md, (CC_SHA256_CTX *)ctx);
	};
	
	anOneShotBlock = ^(const void *data, CC_LONG len, unsigned char *md)
	{
		return CC_SHA224(data, len, md);
	};
	
	CCDigestTestObject* sha224DigestTest = [[[CCDigestTestObject alloc] initWithDigestName:@"SHA224" 
		withDigestSize:CC_SHA224_DIGEST_LENGTH 
		withInitBlock:anInitBlock
		withUpdateBlock:anUpdateBlock
		withFinalBlock:aFinalBlock
		withOneShotBlock:anOneShotBlock] autorelease];
		
	sha224DigestTest.testObject = testObject;
		
	[result addObject:sha224DigestTest];
	
	// --------------------- SHA256 Digest ----------------------
	
	anInitBlock = ^(void *ctx)
	{
		return CC_SHA256_Init((CC_SHA256_CTX *)ctx);
	};
	
	anUpdateBlock = ^(void *ctx, const void *data, CC_LONG len)
	{
		return CC_SHA256_Update((CC_SHA256_CTX *)ctx, data, len);
	};
	
	aFinalBlock = ^(unsigned char *md, void *ctx)
	{
		return CC_SHA256_Final(md, (CC_SHA256_CTX *)ctx);
	};
	
	anOneShotBlock = ^(const void *data, CC_LONG len, unsigned char *md)
	{
		return CC_SHA256(data, len, md);
	};
	
	CCDigestTestObject* sha256DigestTest = [[[CCDigestTestObject alloc] initWithDigestName:@"SHA256" 
		withDigestSize:CC_SHA256_DIGEST_LENGTH 
		withInitBlock:anInitBlock
		withUpdateBlock:anUpdateBlock
		withFinalBlock:aFinalBlock
		withOneShotBlock:anOneShotBlock] autorelease];
		
	sha224DigestTest.testObject = testObject;
		
	[result addObject:sha256DigestTest];
	
	// --------------------- SHA384 Digest ----------------------
	
	anInitBlock = ^(void *ctx)
	{
		return CC_SHA384_Init((CC_SHA512_CTX *)ctx);
	};
	
	anUpdateBlock = ^(void *ctx, const void *data, CC_LONG len)
	{
		return CC_SHA384_Update((CC_SHA512_CTX *)ctx, data, len);
	};
	
	aFinalBlock = ^(unsigned char *md, void *ctx)
	{
		return CC_SHA384_Final(md, (CC_SHA512_CTX *)ctx);
	};
	
	anOneShotBlock = ^(const void *data, CC_LONG len, unsigned char *md)
	{
		return CC_SHA384(data, len, md);
	};
	
	CCDigestTestObject* sha384DigestTest = [[[CCDigestTestObject alloc] initWithDigestName:@"SHA384" 
		withDigestSize:CC_SHA384_DIGEST_LENGTH 
		withInitBlock:anInitBlock
		withUpdateBlock:anUpdateBlock
		withFinalBlock:aFinalBlock
		withOneShotBlock:anOneShotBlock] autorelease];
		
	sha384DigestTest.testObject = testObject;
		
	[result addObject:sha384DigestTest];
	
	// --------------------- SHA512 Digest ----------------------
	
	anInitBlock = ^(void *ctx)
	{
		return CC_SHA512_Init((CC_SHA512_CTX *)ctx);
	};
	
	anUpdateBlock = ^(void *ctx, const void *data, CC_LONG len)
	{
		return CC_SHA512_Update((CC_SHA512_CTX *)ctx, data, len);
	};
	
	aFinalBlock = ^(unsigned char *md, void *ctx)
	{
		return CC_SHA512_Final(md, (CC_SHA512_CTX *)ctx);
	};
	
	anOneShotBlock = ^(const void *data, CC_LONG len, unsigned char *md)
	{
		return CC_SHA512(data, len, md);
	};
	
	CCDigestTestObject* sha512DigestTest = [[[CCDigestTestObject alloc] initWithDigestName:@"SHA512" 
		withDigestSize:CC_SHA512_DIGEST_LENGTH 
		withInitBlock:anInitBlock
		withUpdateBlock:anUpdateBlock
		withFinalBlock:aFinalBlock
		withOneShotBlock:anOneShotBlock] autorelease];
		
	sha512DigestTest.testObject = testObject;
		
	[result addObject:sha512DigestTest];
	
	// --------------------- Skein512 Digest ----------------------

		
	anInitBlock = ^(void *ctx)
	{
		return CCDigestInit(kCCDigestSkein512, (CCDigestCtx *)ctx);
	};
	
	anUpdateBlock = ^(void *ctx, const void *data, CC_LONG len)
	{
		return CCDigestUpdate((CCDigestCtx *)ctx, (const uint8_t *) data, len);
	};
	
	aFinalBlock = ^(unsigned char *md, void *ctx)
	{
		return CCDigestFinal((CCDigestCtx *)ctx, md);
	};
	
	anOneShotBlock = ^(const void *data, CC_LONG len, unsigned char *md)
	{
		return (unsigned char *) CCDigest(kCCDigestSkein512, (const uint8_t *)data, len, (uint8_t *)md);
	};
	
	CCDigestTestObject* skein512DigestTest = [[[CCDigestTestObject alloc] initWithDigestName:@"Skein512 (CommonHash)" 
																			withDigestSize:CC_SHA512_DIGEST_LENGTH 
																			 withInitBlock:anInitBlock
																		   withUpdateBlock:anUpdateBlock
																			withFinalBlock:aFinalBlock
																		  withOneShotBlock:anOneShotBlock] autorelease];
	
	skein512DigestTest.testObject = testObject;
	
	[result addObject:skein512DigestTest];
	

	
	return result;
}


/* --------------------------------------------------------------------------
	method: 		initWithDigestName:withDigestSize:withInitBlock:withUpdateBlock:withFinalBlock:withOneShotBlock:
	returns: 		new CCDigestTestObject object
	parameters:
					name:
						Then name of the digest type i.e. SHA1
					size:
						The size in bytes of the digest for the specified type
					initDigest:
						A block to initialize a staged digest
					updateDigest:
						A block to update a staged digest
					completeDigest:
						A block to finalize a staged digest
					oneShotDigest:
						A block to do a 'one shot' digest
						
												
	decription: 	Initalize a new Digest testing object
   -------------------------------------------------------------------------- */
- (id)initWithDigestName:(NSString *)name withDigestSize:(size_t)size 
	withInitBlock:(initBlock)initDigest
	withUpdateBlock:(updateBlock)updateDigest
	withFinalBlock:(finalBlock)completeDigest
	withOneShotBlock:(oneShotBlock)oneShotDigest
{
	if ((self = [super init]))
	{
		_testPassed = YES;
		[self doAssertTest:(NULL != name) errorString:@"CCDigestTestObject.init received a nil Name"];
		[self doAssertTest:(size > 0) errorString:@"CCDigestTestObject.init got a 0 buffer size"];
		[self doAssertTest:(0 != initDigest) errorString:@"CCDigestTestObject.init received a NULL InitBlock"];
		[self doAssertTest:(0 != updateDigest) errorString:@"CCDigestTestObject.init received a NULL UpdateBlock"];
		[self doAssertTest:(0 != completeDigest) errorString:@"CCDigestTestObject.init received a NULL CompleteDigestBlock"];
		[self doAssertTest:(0 != oneShotDigest) errorString:@"CCDigestTestObject.init received a NULL OneShotBlock"];
		
		_digestName = [name copy];
		_digestSize = size;
		_initBlock = [initDigest copy];
		_updateBlock = [updateDigest copy];
		_finalBlock = [completeDigest copy];
		_oneShotBlock = [oneShotDigest copy];
		
		// Create the data that will be digested by this test.
		CCRandomNumberService* randNumService = [CCRandomNumberService defaultRandomNumberService];
		unsigned int randomDataLength = [randNumService generateRandomNumberInRange:MIN_DATA_SIZE toMax:MAX_DATA_SIZE];
		_digestData = [[randNumService generateRandomDataOfSize:randomDataLength] retain];
		memset(_context, 0, MAX_CONTEXT_SIZE);
	}
	return self;
}

/* --------------------------------------------------------------------------
	method: 		dealloc
	returns: 		void												
	decription: 	Alway put away your toys when you are done playing with
					them.
   -------------------------------------------------------------------------- */
- (void)dealloc
{
	[_digestName release];
	[_stagedResult release];
	[_oneShotResult release];
	[_digestData release];
	[_initBlock release];
	[_updateBlock release];
	[_finalBlock release];
	[_oneShotBlock release];
	[super dealloc];
}

/* --------------------------------------------------------------------------
	method: 		doStaged
	returns: 		void												
	decription: 	Do the staged digest creation for this test placing the
					result into the _stagedResult member
   -------------------------------------------------------------------------- */
- (void)doStaged
{
	
	unsigned int thisMove;
	memset(_context, 0, MAX_CONTEXT_SIZE);
	
	_initBlock(_context);
	
	unsigned int dataLength = [self.digestData length];
	const unsigned char* raw_bytes = (const unsigned char*)[self.digestData bytes];
	
	unsigned char	mdBuffer[MAX_DIGEST_SIZE];
	memset(mdBuffer, 0, MAX_DIGEST_SIZE);
		
	
	CCRandomNumberService* randNumService = [CCRandomNumberService defaultRandomNumberService];
		
	while (dataLength)
	{
		thisMove = [randNumService generateRandomNumberInRange:1 toMax:dataLength];
		_updateBlock(_context, raw_bytes, thisMove);
		
		raw_bytes += thisMove;
		
		dataLength -= thisMove;
	}
	
	(void)_finalBlock(mdBuffer, _context);
	[_stagedResult release];
	_stagedResult = [[NSData alloc] initWithBytes:mdBuffer length:_digestSize];
	
}

/* --------------------------------------------------------------------------
	method: 		doOneShot
	returns: 		void												
	decription: 	Do the 'one shot' digest creation for this test placing the
					result into the _oneShotResult member
   -------------------------------------------------------------------------- */
- (void)doOneShot
{
	unsigned char mdBuffer[MAX_DIGEST_SIZE];
	memset(mdBuffer, 0, MAX_DIGEST_SIZE);
	_oneShotBlock([self.digestData bytes], [self.digestData length], (unsigned char *)mdBuffer);
	[_oneShotResult release];
	_oneShotResult = [[NSData alloc] initWithBytes:mdBuffer length:_digestSize];
	
}

- (void)doAssertTest:(BOOL)result errorString:(NSString *)errorStr
{
	if (nil != self.testObject)
	{
		[self.testObject doAssertTest:result errorString:errorStr];
		return;
	}
	
	if (_testPassed)
	{
		_testPassed = result;
	}
}


/* --------------------------------------------------------------------------
	method: 		runTest
	returns: 		void												
	decription: 	Do the testing of the digest by creating both a staged 
					and one shot digest from the same data and ensuring 
					that the two digests match
   -------------------------------------------------------------------------- */
- (void)runTest
{
	[self doOneShot];
	[self doStaged];
	
	BOOL testResult = [self.stagedResult isEqualToData:self.oneShotResult];
	
	[self doAssertTest:testResult errorString:[
		NSString stringWithFormat:@"Staged Result is not equal to the one shot result for digest type %@", self.digestName]];
	
	if (nil == _testObject)
	{
		printf("DigestTest: %s\n", (self.testPassed) ? "Passed" : "Failed");
	}
}

@end