Index 16 out of bounds; string length 0 in swift - ios

i try to decode some string, which encoded within AES. When the input is string without encode. My app crash. Log:
Terminating app due to uncaught exception 'NSRangeException', reason: '** -[__NSCFConstantString substringFromIndex:]: Index 16 out of bounds; string length 0'*
i used cryptLib to encode and decode
#import "CryptLib.h"
#implementation CryptLib
- (NSData *)encrypt:(NSData *)plainText key:(NSString *)key iv:(NSString *)iv {
char keyPointer[kCCKeySizeAES256+2],// room for terminator (unused) ref: https://devforums.apple.com/message/876053#876053
ivPointer[kCCBlockSizeAES128+2];
BOOL patchNeeded;
bzero(keyPointer, sizeof(keyPointer)); // fill with zeroes for padding
patchNeeded= ([key length] > kCCKeySizeAES256+1);
if(patchNeeded)
{
NSLog(#"Key length is longer %lu", (unsigned long)[[self md5:key] length]);
key = [key substringToIndex:kCCKeySizeAES256]; // Ensure that the key isn't longer than what's needed (kCCKeySizeAES256)
}
//NSLog(#"md5 :%#", key);
[key getCString:keyPointer maxLength:sizeof(keyPointer) encoding:NSUTF8StringEncoding];
[iv getCString:ivPointer maxLength:sizeof(ivPointer) encoding:NSUTF8StringEncoding];
if (patchNeeded) {
keyPointer[0] = '\0'; // Previous iOS version than iOS7 set the first char to '\0' if the key was longer than kCCKeySizeAES256
}
NSUInteger dataLength = [plainText length];
//see https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CCryptorCreateFromData.3cc.html
// For block ciphers, the output size will always be less than or equal to the input size plus the size of one block.
size_t buffSize = dataLength + kCCBlockSizeAES128;
void *buff = malloc(buffSize);
size_t numBytesEncrypted = 0;
//refer to http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonCryptor.h
//for details on this function
//Stateless, one-shot encrypt or decrypt operation.
CCCryptorStatus status = CCCrypt(kCCEncrypt, /* kCCEncrypt, etc. */
kCCAlgorithmAES128, /* kCCAlgorithmAES128, etc. */
kCCOptionPKCS7Padding, /* kCCOptionPKCS7Padding, etc. */
keyPointer, kCCKeySizeAES256, /* key and its length */
ivPointer, /* initialization vector - use random IV everytime */
[plainText bytes], [plainText length], /* input */
buff, buffSize,/* data RETURNED here */
&numBytesEncrypted);
if (status == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];
}
free(buff);
return nil;
}
-(NSData *)decrypt:(NSData *)encryptedText key:(NSString *)key iv:(NSString *)iv {
char keyPointer[kCCKeySizeAES256+2],// room for terminator (unused) ref: https://devforums.apple.com/message/876053#876053
ivPointer[kCCBlockSizeAES128+2];
BOOL patchNeeded;
patchNeeded = ([key length] > kCCKeySizeAES256+1);
if(patchNeeded)
{
NSLog(#"Key length is longer %lu", (unsigned long)[[self md5:key] length]);
key = [key substringToIndex:kCCKeySizeAES256]; // Ensure that the key isn't longer than what's needed (kCCKeySizeAES256)
}
[key getCString:keyPointer maxLength:sizeof(keyPointer) encoding:NSUTF8StringEncoding];
[iv getCString:ivPointer maxLength:sizeof(ivPointer) encoding:NSUTF8StringEncoding];
if (patchNeeded) {
keyPointer[0] = '\0'; // Previous iOS version than iOS7 set the first char to '\0' if the key was longer than kCCKeySizeAES256
}
NSUInteger dataLength = [encryptedText length];
//see https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CCryptorCreateFromData.3cc.html
// For block ciphers, the output size will always be less than or equal to the input size plus the size of one block.
size_t buffSize = dataLength + kCCBlockSizeAES128;
void *buff = malloc(buffSize);
size_t numBytesEncrypted = 0;
//refer to http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonCryptor.h
//for details on this function
//Stateless, one-shot encrypt or decrypt operation.
CCCryptorStatus status = CCCrypt(kCCDecrypt,/* kCCEncrypt, etc. */
kCCAlgorithmAES128, /* kCCAlgorithmAES128, etc. */
kCCOptionPKCS7Padding, /* kCCOptionPKCS7Padding, etc. */
keyPointer, kCCKeySizeAES256,/* key and its length */
ivPointer, /* initialization vector - use same IV which was used for decryption */
[encryptedText bytes], [encryptedText length], //input
buff, buffSize,//output
&numBytesEncrypted);
if (status == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];
}
free(buff);
return nil;
}
- (NSString *) encryptPlainText:(NSString *)plainText key:(NSString *)key iv:(NSString *)iv {
return [[[[CryptLib alloc] init] encrypt:[plainText dataUsingEncoding:NSUTF8StringEncoding] key:[[CryptLib alloc] sha256:key length:32] iv:iv] base64EncodedStringWithOptions:0];
}
- (NSString *) decryptCipherText:(NSString *)ciperText key:(NSString *)key iv:(NSString *)iv {
return [[NSString alloc] initWithData:[[CryptLib alloc] decrypt:[[NSData alloc] initWithBase64EncodedString:ciperText options:NSDataBase64DecodingIgnoreUnknownCharacters] key:[[CryptLib alloc] sha256:key length:32] iv:[[CryptLib alloc] generateRandomIV16]] encoding:NSUTF8StringEncoding];
}
- (NSString *) encryptPlainTextRandomIVWithPlainText:(NSString *)plainText key:(NSString *)key {
CryptLib *crypt = [CryptLib alloc];
NSString *plain = [crypt generateRandomIV16];
plain = [plain stringByAppendingString:plainText];
return [[[crypt init] encrypt:[plain dataUsingEncoding:NSUTF8StringEncoding] key:[crypt sha256:key length:32] iv:[crypt generateRandomIV16]] base64EncodedStringWithOptions:0];
}
- (NSString *) decryptCipherTextRandomIVWithCipherText:(NSString *)cipherText key:(NSString *)key {
CryptLib *crypt = [CryptLib alloc];
NSString *plain = [[NSString alloc] initWithData:[crypt decrypt:[[NSData alloc] initWithBase64EncodedString:cipherText options:NSDataBase64DecodingIgnoreUnknownCharacters] key:[crypt sha256:key length:32] iv:[crypt generateRandomIV16]] encoding:NSUTF8StringEncoding];
return [plain substringFromIndex:16];
}
//this function is no longer used in encryption / decryption
- (NSString *)md5:(NSString *) input
{
const char *cStr = [input UTF8String];
unsigned char digest[16];
CC_MD5( cStr, (uint32_t)strlen(cStr), digest ); // This is the md5 call
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x", digest[i]];
return output;
}
- (NSString*)generateRandomIV:(size_t)length {
// Since the length of Base64 hash is = (3/4) x (length of input string) we can work out input length required to
// generate 32byte hash is 24 bytes long. Note: Base64 may pad end with one, two or no '=' chars if not divisible.
// Therefore we don't care the generated string will be too long, just trim it down before returning.
NSMutableData *data = [NSMutableData dataWithLength:length];
int result = SecRandomCopyBytes(NULL, length, data.mutableBytes);
NSAssert(result == 0, #"Error generating random bytes: %d", errno);
NSString *base64EncodedData = [[data base64EncodedStringWithOptions:0] substringToIndex:length];
return base64EncodedData;
}
- (NSString*)generateRandomIV16 {
// Since the length of Base64 hash is = (3/4) x (length of input string) we can work out input length required to
// generate 32byte hash is 24 bytes long. Note: Base64 may pad end with one, two or no '=' chars if not divisible.
// Therefore we don't care the generated string will be too long, just trim it down before returning.
NSMutableData *data = [NSMutableData dataWithLength:16];
int result = SecRandomCopyBytes(NULL, 16, data.mutableBytes);
NSAssert(result == 0, #"Error generating random bytes: %d", errno);
NSString *base64EncodedData = [[data base64EncodedStringWithOptions:0] substringToIndex:16];
return base64EncodedData;
}
/**
* This function computes the SHA256 hash of input string
* #param key input text whose SHA256 hash has to be computed
* #param length length of the text to be returned
* #return returns SHA256 hash of input text
*/
- (NSString*) sha256:(NSString *)key length:(NSInteger) length{
const char *s=[key cStringUsingEncoding:NSASCIIStringEncoding];
NSData *keyData=[NSData dataWithBytes:s length:strlen(s)];
uint8_t digest[CC_SHA256_DIGEST_LENGTH]={0};
CC_SHA256(keyData.bytes, (CC_LONG)keyData.length, digest);
NSData *out=[NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
NSString *hash=[out debugDescription];
hash = [hash stringByReplacingOccurrencesOfString:#" " withString:#""];
hash = [hash stringByReplacingOccurrencesOfString:#"<" withString:#""];
hash = [hash stringByReplacingOccurrencesOfString:#">" withString:#""];
if(length > [hash length])
{
return hash;
}
else
{
return [hash substringToIndex:length];
}
}
#end
and
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>
#import <Foundation/Foundation.h>
#interface CryptLib : NSObject
- (NSData *)encrypt:(NSData *)plainText key:(NSString *)key iv:(NSString *)iv;
- (NSData *)decrypt:(NSData *)encryptedText key:(NSString *)key iv:(NSString *)iv;
- (NSData *)generateRandomIV:(size_t)length;
- (NSString *) md5:(NSString *) input;
- (NSString *) sha256:(NSString *)key length:(NSInteger) length;
- (NSString *) encryptPlainText:(NSString *)plainText key:(NSString *)key iv:(NSString *)iv;
- (NSString *) decryptCipherText:(NSString *)cipherText key:(NSString *)key iv:(NSString *)iv;
- (NSString *) encryptPlainTextRandomIVWithPlainText:(NSString *)plainText key:(NSString *)key;
- (NSString *) decryptCipherTextRandomIVWithCipherText:(NSString *)cipherText key:(NSString *)key;
#end
My code in my app:
let decryptedString = cryptLib.decryptCipherTextRandomIV(withCipherText: stringValue, key: key)
viewModel.decode = decryptedString
viewModel.showImmunizationImformation.accept(())
i try to handle error but not success:
do {
let decryptedString = try? cryptLib.decryptCipherTextRandomIV(withCipherText: stringValue, key: key)
viewModel.decode = decryptedString
viewModel.showImmunizationImformation.accept(())
} catch {
print("error")
return
}

Related

How can we encrypt bytes through DES/CBC/NOPADDING in Objective C?

I am trying to encrypt bytes data through DES/CBS/NOPADDING algorithm but unable to get correct output. What I have tried is given below in code. Please have a look and suggest.
#import "STEncryptViewController.h"
#import <CommonCrypto/CommonCryptor.h>
#import "STEncryptorDES.h"
#import "NSData+Base64.h"
#import <CommonCrypto/CommonDigest.h>
#define KEY #"MY_TEST_KEY"
#interface STEncryptViewController ()
#end
#implementation STEncryptViewController
#synthesize message;
#synthesize encrypted;
#synthesize decrypted;
#synthesize key;
#synthesize outputMessage;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (IBAction)encrypt:(id)sender
{
const unsigned char bytes[] = {65,17,17,17,17,17,17,17};
NSData *data = [NSData dataWithBytes:bytes length:sizeof(bytes)];
NSString *str = self.key.text;
NSData* data12 = [str dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger len = str.length;
uint8_t *bytes1 = (uint8_t *)[data12 bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:len * 3];
// [result appendString:#"["];
//Key convertion
int i = 0;
while(i < len){
if (i) {
[result appendString:#","];
}
[result appendFormat:#"%d", bytes1[i]];
i++;
}
// [result appendString:#"]"];
NSLog (#"String is %#",str);
NSLog (#"Byte array is %#",result);
//
Byte iv1 [] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
Byte iv [] = {1, 35, 69, 103, -119, -85, -51, -17};
NSData *ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
NSData *encryptedData = [STEncryptorDES encryptData1:data key:[NSData dataWithBytes:iv1 length:sizeof(iv1)] iv:ivData];
NSLog(#"encrypted : %#", [encryptedData base64EncodedString]);
self.outputMessage.text = [NSString stringWithFormat:#"Encrypted String ::>> %#",[encryptedData base64EncodedString]];
[self doCipher:nil enc:kCCEncrypt];
}
- (NSData *)md5DataFromString:(NSString *)input
{
const char *cStr = [input UTF8String];
unsigned char digest[16];
CC_MD5( cStr, strlen(cStr), digest ); // This is the md5 call
return [NSData dataWithBytes:digest length:CC_MD5_DIGEST_LENGTH];
}
- (IBAction)decrypt:(id)sender
{
NSData *valueData = [self hexToBytes:self.decrypted.text];
NSData *keyData = [self md5DataFromString:self.key.text];
Byte iv [] = {1, 35, 69, 103, -119, -85, -51, -17};
NSData *ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
NSData *decryptedData = [STEncryptorDES decryptData:valueData key:keyData iv:ivData];
NSLog(#"decrypted : %#", [[NSString alloc] initWithData:decryptedData encoding:NSASCIIStringEncoding]);
self.outputMessage.text = [NSString stringWithFormat:#"Decrypted String ::>> %#", [[NSString alloc] initWithData:decryptedData encoding:NSASCIIStringEncoding]];
}
- (NSString*)doCipher:(NSString*)plainText enc:(CCOperation)encryptOrDecrypt{
NSData *key123 = [self hexToBytes:#"MY_TEST_KEY"];
const void *vplainText;
size_t plainTextBufferSize;
if (encryptOrDecrypt == kCCDecrypt)
{
NSData *EncryptData = [self hexToBytes:#"5454545454545454"];
// NSData *EncryptData =[NSData dataWithBase64EncodedString:plainText];
plainTextBufferSize = [EncryptData length];
vplainText = [EncryptData bytes];
}
else
{
plainTextBufferSize = [plainText length];
vplainText = (const void *) [plainText UTF8String];
}
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
// uint8_t ivkCCBlockSize3DES;
bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
unsigned char cKey[FBENCRYPT_KEY_SIZE];
bzero(cKey, sizeof(cKey));
[key123 getBytes:cKey length:FBENCRYPT_KEY_SIZE];
//unsigned char result1[24]= {0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,00,00,00,00,0};
unsigned char IV3[8]={1, 35, 69, 103, -119, -85, -51, -17};
uint8_t iv[kCCBlockSize3DES];
memset((void *) iv, 0x0, (size_t) sizeof(iv));
ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithm3DES,
0x0000 ,
cKey, //"123456789012345678901234", //key
kCCKeySize3DES,
IV3 , //iv,
vplainText, //plainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
//if (ccStatus == kCCSuccess) NSLog(#"SUCCESS");
/*else*/ if (ccStatus == kCCParamError) return #"PARAM ERROR";
else if (ccStatus == kCCBufferTooSmall) return #"BUFFER TOO SMALL";
else if (ccStatus == kCCMemoryFailure) return #"MEMORY FAILURE";
else if (ccStatus == kCCAlignmentError) return #"ALIGNMENT";
else if (ccStatus == kCCDecodeError) return #"DECODE ERROR";
else if (ccStatus == kCCUnimplemented) return #"UNIMPLEMENTED";
NSString *result;
if (encryptOrDecrypt == kCCDecrypt)
{
result = [ [NSString alloc] initWithData: [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes] encoding:NSASCIIStringEncoding];
}
else
{
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
NSLog(#"data is: %#", myData);
// result = [NSData base64StringFromData:myData length:myData.length];
// result = [[NSString alloc]initWithData:myData encoding:NSUTF8StringEncoding];
result = nil;
}
return result;
}
-(NSData*)hexToBytes:(NSString *)hex {
NSString *safeStr = [hex stringByReplacingOccurrencesOfString:#"0" withString:#"A"];
NSMutableData* data = [NSMutableData data];
int idx;
for (idx = 0; idx+2 <= safeStr.length; idx+=2) {
NSRange range = NSMakeRange(idx, 2);
NSString* hexStr = [safeStr substringWithRange:range];
NSScanner* scanner = [NSScanner scannerWithString:hexStr];
unsigned int intValue;
[scanner scanHexInt:&intValue];
[data appendBytes:&intValue length:1];
}
NSLog(#"%lu",(unsigned long)data.hash);
return data;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This was my all effort. Currently it is working but not expected output getting. Please suggest me what am I missing here?
One glaring error is that STEncryptorDES is used and is specifies padding but the question states that padding should be used.
Observations:
- You call doCipher with nil and do not use it's return value, delete the unneeded/unused code from the question.
- There is a lot of code to perform a rather simple operation.
- It seems that the test data variable is named iv1, use a better name if that is correct.
- DES should not be used for new work, only when necessary to interoperate with existing work, it is not very secure. New work should use AES.
- #synthesize has not been needed for several years.
In cryptography "DESede/CBC/NoPadding" use following code might be help for you.
Byte iv [] = {1, 35, 69, 103, -119, -85, -51, -17};
NSData *ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
NSData key = ["MY_TEST_KEY" dataUsingEncoding:NSUTF8StringEncoding]
char cKey[key.length];
bzero(cKey, sizeof(cKey));
[key getBytes:cKey length:key.length];
// setup iv
char cIv[kCCBlockSizeDES];
bzero(cIv, kCCBlockSizeDES);
if (ivData) {
[ivData getBytes:cIv length:kCCBlockSizeDES];
}
// setup output buffer
size_t bufferSize = [data length] + kCCBlockSizeDES;
void *buffer = malloc(bufferSize);
// do encrypt
size_t encryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithm3DES,
ccNoPadding,
cKey,
key.length,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&encryptedSize);
if (cryptStatus == kCCSuccess) {
resultData = [NSData dataWithBytes:buffer length:encryptedSize];
free(buffer);
} else {
free(buffer);
NSLog(#"[ERROR] failed to encrypt|CCCryptoStatus: %d", cryptStatus);
return nil;
}

Object-C AES 128 is not encrypted

I encrypt the text "a" using AES 128
I find the source throw the internet below.
I try to change key but always reuslt is same.
Always result is 8e4a3d4beb92d54c7e95f67d41daed59
NSString *key = #"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
// NSString *key = #"00000000000000000000000000000000";
plainText = #"a";
cipherData = [ [ plainText dataUsingEncoding:NSASCIIStringEncoding] AES128EncryptWithKey:key];
NSString * str = [self hexEncode : cipherData ];
NSLog( #"str = %#", str );
- (NSData*) iAESEncrypt:(NSString *)key keySize:(int)keySize {
if(key == nil)
return nil;
char keyPtr[keySize+1];
bzero( keyPtr, sizeof(keyPtr) );
[key getCString: keyPtr maxLength: sizeof(keyPtr) encoding:NSUTF8StringEncoding];
size_t numBytesEncrypted = 0x00;
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
CCCryptorStatus result = CCCrypt( kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionECBMode | kCCOptionPKCS7Padding,
keyPtr,
keySize,
NULL /*iv*/,
[self bytes], [self length],
buffer, bufferSize,
&numBytesEncrypted );
if( result == kCCSuccess )
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
else
NSLog(#"iAESEncrypt FAIL!");
free(buffer);
return nil;
}
-(NSString *) hexEncode:(NSData*)data
{
NSMutableString *hex = [NSMutableString string];
unsigned char *bytes = (unsigned char *)[data bytes];
char temp[3];
NSUInteger i = 0;
for (i = 0; i < [data length]; i++)
{
temp[0] = temp[1] = temp[2] = 0;
(void)sprintf(temp, "%02x", bytes[i]);
[hex appendString:[NSString stringWithUTF8String:temp]];
}
return hex;
}
- (NSData*)AES128EncryptWithKey:(NSString*)key
{
return [self iAESEncrypt:key keySize:kCCKeySizeAES128];
}
Your key variable has 32 length which is probably a Hex data. So first, you must convert it to NSData with length of kCCKeySizeAES128 (16 bytes).
You can use your hex decoder code or thank to hex conversion code here https://stackoverflow.com/a/7318062/296651 :
....
key = [key stringByReplacingOccurrencesOfString:#" " withString:#""];
NSMutableData *commandToSend= [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [key length]/2; i++) {
byte_chars[0] = [key characterAtIndex:i*2];
byte_chars[1] = [key characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[commandToSend appendBytes:&whole_byte length:1];
}
size_t numBytesEncrypted = 0x00;
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
memset(buffer, 0, bufferSize);
CCCryptorStatus result = CCCrypt( kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionECBMode | kCCOptionPKCS7Padding,
[commandToSend bytes],
keySize,
NULL /*iv*/,
[self bytes], [self length],
buffer, bufferSize,
&numBytesEncrypted );
....
You're always encrypt with an empty string.
[key getCString: keyPtr maxLength: sizeof(keyPtr) encoding:NSUTF8StringEncoding];
The sizeof(keyPtr) is always 4...
Use
[key getCString: keyPtr maxLength: keySize encoding:NSUTF8StringEncoding];
And getCString: is returning a BOOL result.. In your case it is always false, but the code posted does not check it.
There are several AES implementation, which are right. And may I recommend to use AES256 instead of 128. It is much more secure.

Storing and Retrieving compressed texts as BLOB from Sqlite via FMDB

I am trying to store text data as compressed blobs.
[db executeUpdate:#"create table blobTable (a text, b blob)"];
[db executeUpdate:#"insert into blobTable (a, b) values (?, compressMe('random text string'))", #"lord of the rings"];
And then I try to query them using:
FMResultSet *rs = [db executeQuery:#"select uncompressMe(b) as k from blobTable where a = ?", #"lord of the rings"];
Where compressMe and uncompressMe are defined as:
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
[queue inDatabase:^(FMDatabase *adb) {
[adb makeFunctionNamed:#"compressMe" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
if (sqlite3_value_type(aargv[0]) == SQLITE_TEXT) {
#autoreleasepool {
const char *c = (const char *)sqlite3_value_text(aargv[0]);
NSString *s = [NSString stringWithUTF8String:c];
NSLog(#"string to compress: %#", s);
NSData *compressedBody = [self gzipDeflate:[s dataUsingEncoding:NSUTF8StringEncoding]];
sqlite3_result_blob(context, (__bridge const void *)(compressedBody), [compressedBody length], nil);
}
}
else {
NSLog(#"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
sqlite3_result_null(context);
}
}];
}];
[queue inDatabase:^(FMDatabase *adb) {
[adb makeFunctionNamed:#"uncompressMe" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
if (sqlite3_value_type(aargv[0]) == SQLITE_BLOB) {
#autoreleasepool {
NSLog(#"inside uncompressMe");
NSUInteger len = sqlite3_value_bytes(aargv[0]);
Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, sqlite3_value_blob(aargv[0]), len);
NSData *data = [[NSData alloc] initWithBytes:byteData length:len];
NSData *deflatedBody = [self gzipInflate:data];
NSString *deflatedString = [[NSString alloc] initWithData:deflatedBody encoding:NSUTF8StringEncoding];
sqlite3_result_text(context, (__bridge const void *)(deflatedString), -1, SQLITE_UTF8);
}
}
else {
NSLog(#"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
sqlite3_result_null(context);
}
}];
}];
For the sake of completeness, the zip functions are defined as such:
- (NSData *)gzipInflate:(NSData*)data
{
if ([data length] == 0) return data;
unsigned full_length = [data length];
unsigned half_length = [data length] / 2;
NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
BOOL done = NO;
int status;
z_stream strm;
strm.next_in = (Bytef *)[data bytes];
strm.avail_in = [data length];
strm.total_out = 0;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
while (!done)
{
// Make sure we have enough room and reset the lengths.
if (strm.total_out >= [decompressed length])
[decompressed increaseLengthBy: half_length];
strm.next_out = [decompressed mutableBytes] + strm.total_out;
strm.avail_out = [decompressed length] - strm.total_out;
// Inflate another chunk.
status = inflate (&strm, Z_SYNC_FLUSH);
if (status == Z_STREAM_END) done = YES;
else if (status != Z_OK) break;
}
if (inflateEnd (&strm) != Z_OK) return nil;
// Set real length.
if (done)
{
[decompressed setLength: strm.total_out];
return [NSData dataWithData: decompressed];
}
else return nil;
}
- (NSData *)gzipDeflate:(NSData*)data
{
if ([data length] == 0) return data;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.total_out = 0;
strm.next_in=(Bytef *)[data bytes];
strm.avail_in = [data length];
// Compresssion Levels:
// Z_NO_COMPRESSION
// Z_BEST_SPEED
// Z_BEST_COMPRESSION
// Z_DEFAULT_COMPRESSION
if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion
do {
if (strm.total_out >= [compressed length])
[compressed increaseLengthBy: 16384];
strm.next_out = [compressed mutableBytes] + strm.total_out;
strm.avail_out = [compressed length] - strm.total_out;
deflate(&strm, Z_FINISH);
} while (strm.avail_out == 0);
deflateEnd(&strm);
[compressed setLength: strm.total_out];
return [NSData dataWithData:compressed];
}
However, the uncompressMe function doesnt seem to succeed. It fails in the Inflate method, which always returns a nil. Any thoughts what I might be doing wrong? I have checked to make sure that the blob column does get populated.
I feel like issue is from below code:
NSUInteger len = sqlite3_value_bytes(aargv[0]);
Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, sqlite3_value_blob(aargv[0]), len);
NSData *data = [[NSData alloc] initWithBytes:byteData length:len];
Try this to load NSData from database instead of creating byte variable and copying it.
const void *bytes = sqlite3_column_blob(statement, 3);
NSData *data = [[NSData alloc] initWithBytes:bytes length:size];

AES 128 with CBC

I'm going mad with a simple AES 128 in Objective C and there's no way to get the expected ciphered text in a simple test. Could anyone tell me what am I doing wrong?
Test:
- (void)testAES128_1
{
NSString *testVector = #"6bc1bee22e409f96e93d7e117393172a";
NSString *initVector = #"000102030405060708090A0B0C0D0E0F";
NSString *key = #"2b7e151628aed2a6abf7158809cf4f3c";
NSString *expected = #"7649abac8119b246cee98e9b12e9197d";
NSData *inputData = [self dataFromHexString:testVector];
NSData *keyData = [self dataFromHexString:key];
NSData *expectedData = [self dataFromHexString:expected];
NSData *current = [inputData AES128EncryptWithIV:initVector andKey:key];
// What I get in current = cf2ea38a123be20765eb8c5c56caf224 != expected
BOOL res = [inputData isEqualToData:current];
XCTAssertTrue(res);
}
// For Converting incoming HexString into NSData
- (NSData *)dataFromHexString:(NSString *)string
{
NSMutableData *stringData = [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [string length] / 2; i++) {
byte_chars[0] = [string characterAtIndex:i*2];
byte_chars[1] = [string characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[stringData appendBytes:&whole_byte length:1];
}
return stringData;
}
Category NSData (AES):
- (NSData *)AES128EncryptWithIV:(NSString *)iv andKey:(NSString *)key {
char ivPtr[kCCKeySizeAES128 + 1];
bzero(ivPtr, sizeof(ivPtr));
// fetch iv data
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, 0,
keyPtr, kCCKeySizeAES128,
ivPtr /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
I am working with these set of AES test vectors:
http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors#aes-cbc-128
Thanks
Simply you are not converting the iv in hex ascii to an NSData. The example test vector have an iv so to obtain the matching cipher text you need to use the Initialization vector.
Note: The line:
BOOL res = [inputData isEqualToData:current];
should be:
BOOL res = [expectedData isEqualToData:current];
Note: The output block count is not larger if there is no padding.
Here is my test code:
No need for a Category, I just make there methods class methods.
+ (void)testAES128_1 {
NSString *testVector = #"6bc1bee22e409f96e93d7e117393172a";
NSString *initVector = #"000102030405060708090A0B0C0D0E0F";
NSString *key = #"2b7e151628aed2a6abf7158809cf4f3c";
NSString *expected = #"7649abac8119b246cee98e9b12e9197d";
NSData *inputData = [self dataFromHexString:testVector];
NSData *keyData = [self dataFromHexString:key];
NSData *ivData = [self dataFromHexString:initVector];
NSData *expectedData = [self dataFromHexString:expected];
NSError *error;
NSData *current = [Test doCipher:inputData
iv:ivData
key:keyData
context:kCCEncrypt
error:&error];
BOOL res = [expectedData isEqualToData:current];
NSLog(#"Match: %#", res ? #"Yes" : #"No"); // Match: Yes
}
+ (NSData *)doCipher:(NSData *)dataIn
iv:(NSData *)iv
key:(NSData *)symmetricKey
context:(CCOperation)encryptOrDecrypt // kCCEncrypt or kCCDecrypt
error:(NSError **)error
{
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];
ccStatus = CCCrypt( encryptOrDecrypt,
kCCAlgorithmAES128,
0, //kCCOptionPKCS7Padding,
symmetricKey.bytes,
kCCKeySizeAES128,
iv.bytes,
dataIn.bytes,
dataIn.length,
dataOut.mutableBytes,
dataOut.length,
&cryptBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes;
}
else {
if (error) {
*error = [NSError errorWithDomain:#"kEncryptionError"
code:ccStatus
userInfo:nil];
}
dataOut = nil;
}
return dataOut;
}

128 bit AES encryption returns null in Objective-C

I am trying to encrypt a string using AES 128 bit encryption, but the encrypted data converted, when converted to a string, always returns null.
NSString *iv = #"fedcba9876543210";
NSString *key = #"0123456789abcdef";
- (NSData *)AES128EncryptWithKey
{
char keyPtr[kCCKeySizeAES128 + 1];
bzero( keyPtr, sizeof( keyPtr ) );
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode | kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL ,
[self bytes], dataLength,
buffer, bufferSize,
&numBytesEncrypted );
if( cryptStatus == kCCSuccess )
{
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
}
This is my encryption function.
NSData *data = [#"String to encrypt" dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData = [[NSData alloc] init];
encryptedData = [data AES128EncryptWithKey];
NSLog(#"Encrypted Data Length %d", [encryptedData length]);
if (encryptedData != nil)
{
NSString* myString;
myString = [[NSString alloc] initWithData:encryptedData encoding:NSUTF8StringEncoding];
NSString* newStr = [NSString stringWithUTF8String:[encryptedData bytes]];
}
After encryption, encryption data length is reported as 16 but after converting it to an NSString it returns null. If I use stringWithUTF16String to convert it, it returns like "軽ﶁែ뼐끨驂퐇". What is the issue here?
try this code
#define kCryptingKey #"1234567890abcdef"
#implementation Tool
+ (NSString*) crypt:(NSString*)recource
{
NSData *data = [recource dataUsingEncoding:NSUTF8StringEncoding];
NSData *encrypt = [self AES128EncryptWithKey:kCryptingKey withData:data];
return [self stringWithHexBytes:encrypt];
}
+ (NSString*) decryptData:(NSData*)recource
{
NSData *decrypt = [self AES128DecryptWithKey:kCryptingKey withData:recource];
return [[NSString alloc] initWithData:decrypt encoding:NSUTF8StringEncoding];
}
+ (NSString*) decrypt:(NSString*)recource
{
NSData* data=[self decodeFromHexidecimal:recource];
NSData *decrypt = [self AES128DecryptWithKey:kCryptingKey withData:data];
return [[NSString alloc] initWithData:decrypt encoding:NSUTF8StringEncoding];
}
+ (NSData *) decodeFromHexidecimal:(NSString*)str
{
NSString *command = [NSString stringWithString:str];
command = [command stringByReplacingOccurrencesOfString:#" " withString:#""];
NSMutableData *commandToSend = [[NSMutableData data] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [command length]/2; i++) {
byte_chars[0] = [command characterAtIndex:i*2];
byte_chars[1] = [command characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[commandToSend appendBytes:&whole_byte length:1];
}
return commandToSend;
}
+ (NSData *)AES128EncryptWithKey:(NSString *)key withData:(NSData*)_data
{
// ‘key’ should be 16 bytes for AES128
char keyPtr[kCCKeySizeAES128 + 1]; // room for terminator (unused)
bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [_data length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That’s why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode | kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[_data bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted );
if( cryptStatus == kCCSuccess )
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free( buffer ); //free the buffer
return nil;
}
+ (NSData *)AES128DecryptWithKey:(NSString *)key withData:(NSData*)data
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionECBMode | kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[data bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
free(buffer); //free the buffer;
return nil;
}
+ (NSString*) stringWithHexBytes:(NSData*)_data {
NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([_data length] * 2)];
const unsigned char *dataBuffer = [_data bytes];
int i;
for (i = 0; i < [_data length]; ++i) {
[stringBuffer appendFormat:#"%02lX", (unsigned long)dataBuffer[i]];
}
return [[stringBuffer copy] autorelease];
}
#end
Finally I got it.. As Joachim suggested I used the following method and it worked..
unichar* hexChars = (unichar*)malloc(sizeof(unichar) * (data.length*2));
unsigned char* bytes = (unsigned char*)data.bytes;
for (NSUInteger i = 0; i < data.length; i++) {
unichar c = bytes[i] / 16;
if (c < 10) c += '0';
else c += 'a' - 10;
hexChars[i*2] = c;
c = bytes[i] % 16;
if (c < 10) c += '0';
else c += 'a' - 10;
hexChars[i*2+1] = c;
}
NSString* retVal = [[NSString alloc] initWithCharactersNoCopy:hexChars
length:data.length*2
freeWhenDone:YES];

Resources